I've been working in the conversion system lately, and discovered a
problem with it's DCL anti-pattern usage.  This is not meant to single
out the conversion framework, it's just a convient example.

In Converters.getConverter(Class, Class), there is this pattern:

===
Converter result = convertMap.get(key);
if (result == null) {
    if (!noConversions.contains(key)) {
        synchronized (converterMap) {
            ....
            if (condition) {
                converterMap.put(key, converter);
            }
            ...
            noConversions.add(key);
            ...
        }
    }
}
===

Here are the issues with this code.

No protection on the first get.  This can break if thread has hit
asking for this particular key first, enters the synchronized block,
calls put, and the map starts to rehash itself.  In the middle of this
rehashing, a second comes in, and tries to find the key.  It's
possible that the get could go into a never-ending loop.

There is no protection at all on the noConversions set.  That is just
plain bad.

Now, it must be said, that there are ways to protect against this.
First, place everything inside synchronization.  This has issues which
we are all aware of.  Second, use a concurrent algorithm.  I am not
aware how much FastMap/FastList/FastSet obey this.  ConcurrentHashMap
gets around it by having 32 internal maps; each internal map *does*
have synchronization around it.

The other main thing to keep in mind, that if the value placed into
the map is a read-only kind of thing, doesn't have any mutative state,
and the creation of the value has no side affects, then you don't have
to have any protection to make certain there is only one instance of
value created.  However, that still doesn't change the fact that the
above anti-pattern *can* dead-lock.


Reply via email to