mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8187408: AbstractQueuedSynchronizer wait queue corrupted when thread awaits without holding the lock
Reviewed-by: martin, psandoz, dholmes
This commit is contained in:
parent
dfce305868
commit
c6e3667228
5 changed files with 122 additions and 37 deletions
|
@ -67,11 +67,11 @@ public abstract class AbstractQueuedLongSynchronizer
|
|||
private static final long serialVersionUID = 7373984972572414692L;
|
||||
|
||||
/*
|
||||
To keep sources in sync, the remainder of this source file is
|
||||
exactly cloned from AbstractQueuedSynchronizer, replacing class
|
||||
name and changing ints related with sync state to longs. Please
|
||||
keep it that way.
|
||||
*/
|
||||
* To keep sources in sync, the remainder of this source file is
|
||||
* exactly cloned from AbstractQueuedSynchronizer, replacing class
|
||||
* name and changing ints related with sync state to longs. Please
|
||||
* keep it that way.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new {@code AbstractQueuedLongSynchronizer} instance
|
||||
|
@ -725,8 +725,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
|||
/**
|
||||
* Returns {@code true} if synchronization is held exclusively with
|
||||
* respect to the current (calling) thread. This method is invoked
|
||||
* upon each call to a non-waiting {@link ConditionObject} method.
|
||||
* (Waiting methods instead invoke {@link #release}.)
|
||||
* upon each call to a {@link ConditionObject} method.
|
||||
*
|
||||
* <p>The default implementation throws {@link
|
||||
* UnsupportedOperationException}. This method is invoked
|
||||
|
@ -1366,9 +1365,8 @@ public abstract class AbstractQueuedLongSynchronizer
|
|||
}
|
||||
|
||||
/**
|
||||
* Condition implementation for a {@link
|
||||
* AbstractQueuedLongSynchronizer} serving as the basis of a {@link
|
||||
* Lock} implementation.
|
||||
* Condition implementation for a {@link AbstractQueuedLongSynchronizer}
|
||||
* serving as the basis of a {@link Lock} implementation.
|
||||
*
|
||||
* <p>Method documentation for this class describes mechanics,
|
||||
* not behavioral specifications from the point of view of Lock
|
||||
|
@ -1401,6 +1399,8 @@ public abstract class AbstractQueuedLongSynchronizer
|
|||
* @return its new wait node
|
||||
*/
|
||||
private Node addConditionWaiter() {
|
||||
if (!isHeldExclusively())
|
||||
throw new IllegalMonitorStateException();
|
||||
Node t = lastWaiter;
|
||||
// If lastWaiter is cancelled, clean out.
|
||||
if (t != null && t.waitStatus != Node.CONDITION) {
|
||||
|
|
|
@ -194,19 +194,13 @@ import java.util.concurrent.TimeUnit;
|
|||
* represent the locked state. While a non-reentrant lock
|
||||
* does not strictly require recording of the current owner
|
||||
* thread, this class does so anyway to make usage easier to monitor.
|
||||
* It also supports conditions and exposes
|
||||
* one of the instrumentation methods:
|
||||
* It also supports conditions and exposes some instrumentation methods:
|
||||
*
|
||||
* <pre> {@code
|
||||
* class Mutex implements Lock, java.io.Serializable {
|
||||
*
|
||||
* // Our internal helper class
|
||||
* private static class Sync extends AbstractQueuedSynchronizer {
|
||||
* // Reports whether in locked state
|
||||
* protected boolean isHeldExclusively() {
|
||||
* return getState() == 1;
|
||||
* }
|
||||
*
|
||||
* // Acquires the lock if state is zero
|
||||
* public boolean tryAcquire(int acquires) {
|
||||
* assert acquires == 1; // Otherwise unused
|
||||
|
@ -220,14 +214,27 @@ import java.util.concurrent.TimeUnit;
|
|||
* // Releases the lock by setting state to zero
|
||||
* protected boolean tryRelease(int releases) {
|
||||
* assert releases == 1; // Otherwise unused
|
||||
* if (getState() == 0) throw new IllegalMonitorStateException();
|
||||
* if (!isHeldExclusively())
|
||||
* throw new IllegalMonitorStateException();
|
||||
* setExclusiveOwnerThread(null);
|
||||
* setState(0);
|
||||
* return true;
|
||||
* }
|
||||
*
|
||||
* // Reports whether in locked state
|
||||
* public boolean isLocked() {
|
||||
* return getState() != 0;
|
||||
* }
|
||||
*
|
||||
* public boolean isHeldExclusively() {
|
||||
* // a data race, but safe due to out-of-thin-air guarantees
|
||||
* return getExclusiveOwnerThread() == Thread.currentThread();
|
||||
* }
|
||||
*
|
||||
* // Provides a Condition
|
||||
* Condition newCondition() { return new ConditionObject(); }
|
||||
* public Condition newCondition() {
|
||||
* return new ConditionObject();
|
||||
* }
|
||||
*
|
||||
* // Deserializes properly
|
||||
* private void readObject(ObjectInputStream s)
|
||||
|
@ -240,12 +247,17 @@ import java.util.concurrent.TimeUnit;
|
|||
* // The sync object does all the hard work. We just forward to it.
|
||||
* private final Sync sync = new Sync();
|
||||
*
|
||||
* public void lock() { sync.acquire(1); }
|
||||
* public boolean tryLock() { return sync.tryAcquire(1); }
|
||||
* public void unlock() { sync.release(1); }
|
||||
* public Condition newCondition() { return sync.newCondition(); }
|
||||
* public boolean isLocked() { return sync.isHeldExclusively(); }
|
||||
* public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
|
||||
* public void lock() { sync.acquire(1); }
|
||||
* public boolean tryLock() { return sync.tryAcquire(1); }
|
||||
* public void unlock() { sync.release(1); }
|
||||
* public Condition newCondition() { return sync.newCondition(); }
|
||||
* public boolean isLocked() { return sync.isLocked(); }
|
||||
* public boolean isHeldByCurrentThread() {
|
||||
* return sync.isHeldExclusively();
|
||||
* }
|
||||
* public boolean hasQueuedThreads() {
|
||||
* return sync.hasQueuedThreads();
|
||||
* }
|
||||
* public void lockInterruptibly() throws InterruptedException {
|
||||
* sync.acquireInterruptibly(1);
|
||||
* }
|
||||
|
@ -1193,8 +1205,7 @@ public abstract class AbstractQueuedSynchronizer
|
|||
/**
|
||||
* Returns {@code true} if synchronization is held exclusively with
|
||||
* respect to the current (calling) thread. This method is invoked
|
||||
* upon each call to a non-waiting {@link ConditionObject} method.
|
||||
* (Waiting methods instead invoke {@link #release}.)
|
||||
* upon each call to a {@link ConditionObject} method.
|
||||
*
|
||||
* <p>The default implementation throws {@link
|
||||
* UnsupportedOperationException}. This method is invoked
|
||||
|
@ -1834,9 +1845,8 @@ public abstract class AbstractQueuedSynchronizer
|
|||
}
|
||||
|
||||
/**
|
||||
* Condition implementation for a {@link
|
||||
* AbstractQueuedSynchronizer} serving as the basis of a {@link
|
||||
* Lock} implementation.
|
||||
* Condition implementation for a {@link AbstractQueuedSynchronizer}
|
||||
* serving as the basis of a {@link Lock} implementation.
|
||||
*
|
||||
* <p>Method documentation for this class describes mechanics,
|
||||
* not behavioral specifications from the point of view of Lock
|
||||
|
@ -1867,6 +1877,8 @@ public abstract class AbstractQueuedSynchronizer
|
|||
* @return its new wait node
|
||||
*/
|
||||
private Node addConditionWaiter() {
|
||||
if (!isHeldExclusively())
|
||||
throw new IllegalMonitorStateException();
|
||||
Node t = lastWaiter;
|
||||
// If lastWaiter is cancelled, clean out.
|
||||
if (t != null && t.waitStatus != Node.CONDITION) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue