Update: When I first posted this, I was fairly certain the code was broken. Now, I'm no longer sure about what I'd observed. The biggest problem I'm having is that I can't seem to apply 17.4. Memory Model and state straight out whether it should or shouldn't work.
This following code is broken.
It's overly complex for what it's trying to achieve, but furthermore, it's thread-unsafe in that I've observed that it can indefinitely wait at c
. I'm not worried about the former (one could use a ReentrantLock
or CountDownLatch
for sounder code), but I'm wondering, what's the reason for the latter?
static final ConcurrentHashMap<Integer, Object> mutex = new ConcurrentHashMap<>();
public static brokenFoo() {
Object ourLock = new Object();
for (;;) {
Object theirLock = mutex.putIfAbsent(0, ourLock);
if (theirLock == null) {
break;
}
synchronized (theirLock) { // a
if (mutex.get(0) != theirLock) { // b
continue;
}
theirLock.wait(); // c
} // d
}
try {
// critical section
} finally {
synchronized (ourLock) { // e
mutex.remove(0); // f
ourLock.notifyAll(); // g
} // h
}
}
I've thought in terms of happens-befores:
- hb(f, h) and hb(h, a) therefore hb(f, a)
- hb(c, d) and hb(d, e) therefore hb(c, e)
But, this doesn't seem to prove or disprove anything.
Edit: (Above question fails to really explain what this code should do.)
Expected:
brokenFoo()
is called by multiple threads and the above code is supposed to provide mutual exclusion over// critical section
.- If two or more threads enter
brokenFoo()
at the same time, only one should proceed to// critical section
, while others wait somewhere prior. - After the thread in
// critical section
has exited, another should proceed to take its place.
Actual:
- It's been observed that there're threads that are waiting at
c
even though no other threads are inbrokenFoo()
.