Question reg. holding Locks during invocation of Lambdas
dl at cs.oswego.edu
Tue Jun 18 17:02:03 PDT 2013
On 06/18/13 10:43, Gernot Neppert wrote:
> Hi Doug, thanks for the answer. However, you seem to answer only why
> "computeIfAbsent" was not initially included in ConcurrentMap's interface and
> why it has been included now.
> The point I was trying to make is that you can have it both ways:
> 1.) implement correct atomic behaviour of the entire compound operation.
> 2.) Call the user-supplied callback without holding any internal Locks.
> I'd simply like to know the reason for this decision. For example, "Too high a
> performance penalty for other operations" would be an acceptable answer!
Ultimately yes. the use of a global lock would negate the
Concurrentness of most ConcurrentMaps.
(We do document use of locks here. And people who would rather
for example use reservation sentinels can still get these
effects without locking using regular putIfAbsent/replace mechanics.)
> 2013/6/18 Doug Lea <dl at cs.oswego.edu <mailto:dl at cs.oswego.edu>>
> On 06/18/13 05:11, Gernot Neppert wrote:
> > Hi all,
> > I noticed that ConcurrentHashMap.computeIfAbsent() invokes the
> > user-supplied callback while being in a synchronized block.
> > One of the rules I stick to in programming is "Don't invoke a user-supplied
> > callback from a library while holding a Lock".
> > (because it can lead to nasty deadlocks).
> Yes, this is exactly why we did not originally provide
> such a method in ConcurrentMap. But now we know that this
> was the wrong choice. Empirical reports imply that many more
> bugs are caused by people not correctly maintaining atomicity
> when they don't have this method than would be caused by
> supporting it and thus making it easier to encounter deadlock.
> (See for example Shacham et al's OOPSLA 2011 paper
> at http://www.cs.tau.ac.il/~ohads/)
> > If I understand it correctly, the purpose of computeIfAbsent() is delaying
> > the invocation of a possibly lengthy factory-expression until it has been
> > determined that a mapping for the key is not already present.
> > Thus, shouldn't the callback better be called outside any held Locks?
> > For those interested in more details:
> > A while back I wrote a class "CacheMap" that serves as a
> > drop-in-replacement for java.util.Map. Its get(Object key) method invokes a
> > uses-supplied callback if a mapping is not already present.
> > In the implementation I went to great lengths to ensure that no Locks are
> > held while invoking the callback while at the same time guaranteeing
> > consistency in a multi-threaded scenario.
> > (The Map-wide Lock is only held for a short duration to atomically check
> > for the key and put a temporary "factory" object in the internal Map. This
> > factory object then uses wait/notify to ensure that subsequent threads
> > requiring the same key will block until the callback has been invoked).
> > So I know it's possible to do it "correctly". Now I'd be reluctant to
> > replace my "CacheMap" with JDK's "ConcurrentHashMap" because of the
> > possible deadlocks waiting to happen.
More information about the lambda-dev