Martin Buchholz reports:
While writing a test for this, I unearthed yet another race condition in AQS.
Fortunately, it's in new jdk7 code.
In the expression
(h = head) != tail &&
head may be read as null,
then head and tail are both initialized before tail on RHS is read,
yielding NPE
Caused by: java.lang.NullPointerException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.hasQueuedPredecessors(AbstractQueuedSynchronizer.java:1510)
at java.util.concurrent.Semaphore$FairSync.tryAcquireShared(Semaphore.java:245)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1263)
at java.util.concurrent.Semaphore.acquire(Semaphore.java:312)
We need to read fields in the reverse order.
@@ -1445,8 +1502,10 @@
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
- Node h, s;
- return (h = head) != tail &&
+ Node t = tail; // Read fields in reverse initialization order
+ Node h = head;
+ Node s;
+ return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}