ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/ReentrantLockTest.java
(Generate patch)

Comparing jsr166/src/test/tck/ReentrantLockTest.java (file contents):
Revision 1.65 by jsr166, Sun May 14 02:03:15 2017 UTC vs.
Revision 1.70 by jsr166, Sun Sep 22 01:59:57 2019 UTC

# Line 8 | Line 8
8  
9   import static java.util.concurrent.TimeUnit.MILLISECONDS;
10  
11 + import java.util.ArrayList;
12   import java.util.Arrays;
13   import java.util.Collection;
14   import java.util.HashSet;
15 + import java.util.concurrent.Callable;
16   import java.util.concurrent.CountDownLatch;
17   import java.util.concurrent.CyclicBarrier;
18 + import java.util.concurrent.ThreadLocalRandom;
19 + import java.util.concurrent.atomic.AtomicBoolean;
20   import java.util.concurrent.locks.Condition;
21 + import java.util.concurrent.locks.Lock;
22   import java.util.concurrent.locks.ReentrantLock;
23  
19 import junit.framework.AssertionFailedError;
24   import junit.framework.Test;
25   import junit.framework.TestSuite;
26  
27 + @SuppressWarnings("WaitNotInLoop") // we implement spurious-wakeup freedom
28   public class ReentrantLockTest extends JSR166TestCase {
29      public static void main(String[] args) {
30          main(suite(), args);
# Line 85 | Line 90 | public class ReentrantLockTest extends J
90          long startTime = System.nanoTime();
91          while (!lock.hasQueuedThread(t)) {
92              if (millisElapsedSince(startTime) > LONG_DELAY_MS)
93 <                throw new AssertionFailedError("timed out");
93 >                throw new AssertionError("timed out");
94              Thread.yield();
95          }
96          assertTrue(t.isAlive());
# Line 145 | Line 150 | public class ReentrantLockTest extends J
150  
151      enum AwaitMethod { await, awaitTimed, awaitNanos, awaitUntil }
152  
153 +    static AwaitMethod randomAwaitMethod() {
154 +        AwaitMethod[] awaitMethods = AwaitMethod.values();
155 +        return awaitMethods[ThreadLocalRandom.current().nextInt(awaitMethods.length)];
156 +    }
157 +
158      /**
159       * Awaits condition "indefinitely" using the specified AwaitMethod.
160       */
# Line 1132 | Line 1142 | public class ReentrantLockTest extends J
1142          lock.unlock();
1143          assertTrue(lock.toString().contains("Unlocked"));
1144      }
1145 +
1146 +    /**
1147 +     * Tests scenario for JDK-8187408
1148 +     * AbstractQueuedSynchronizer wait queue corrupted when thread awaits without holding the lock
1149 +     */
1150 +    public void testBug8187408() throws InterruptedException {
1151 +        final ThreadLocalRandom rnd = ThreadLocalRandom.current();
1152 +        final AwaitMethod awaitMethod = randomAwaitMethod();
1153 +        final int nThreads = rnd.nextInt(2, 10);
1154 +        final ReentrantLock lock = new ReentrantLock();
1155 +        final Condition cond = lock.newCondition();
1156 +        final CountDownLatch done = new CountDownLatch(nThreads);
1157 +        final ArrayList<Thread> threads = new ArrayList<>();
1158 +
1159 +        Runnable rogue = () -> {
1160 +            while (done.getCount() > 0) {
1161 +                try {
1162 +                    // call await without holding lock?!
1163 +                    await(cond, awaitMethod);
1164 +                    throw new AssertionError("should throw");
1165 +                }
1166 +                catch (IllegalMonitorStateException success) {}
1167 +                catch (Throwable fail) { threadUnexpectedException(fail); }}};
1168 +        Thread rogueThread = new Thread(rogue, "rogue");
1169 +        threads.add(rogueThread);
1170 +        rogueThread.start();
1171 +
1172 +        Runnable waiter = () -> {
1173 +            lock.lock();
1174 +            try {
1175 +                done.countDown();
1176 +                cond.await();
1177 +            } catch (Throwable fail) {
1178 +                threadUnexpectedException(fail);
1179 +            } finally {
1180 +                lock.unlock();
1181 +            }};
1182 +        for (int i = 0; i < nThreads; i++) {
1183 +            Thread thread = new Thread(waiter, "waiter");
1184 +            threads.add(thread);
1185 +            thread.start();
1186 +        }
1187 +
1188 +        assertTrue(done.await(LONG_DELAY_MS, MILLISECONDS));
1189 +        lock.lock();
1190 +        try {
1191 +            assertEquals(nThreads, lock.getWaitQueueLength(cond));
1192 +        } finally {
1193 +            cond.signalAll();
1194 +            lock.unlock();
1195 +        }
1196 +        for (Thread thread : threads) {
1197 +            thread.join(LONG_DELAY_MS);
1198 +            assertFalse(thread.isAlive());
1199 +        }
1200 +    }
1201 +
1202 +    /**
1203 +     * ThreadMXBean reports the blockers that we expect.
1204 +     */
1205 +    public void testBlockers() {
1206 +        if (!testImplementationDetails) return;
1207 +        final boolean fair = randomBoolean();
1208 +        final boolean timedAcquire = randomBoolean();
1209 +        final boolean timedAwait = randomBoolean();
1210 +        final String syncClassName = fair
1211 +            ? "ReentrantLock$FairSync"
1212 +            : "ReentrantLock$NonfairSync";
1213 +        final String conditionClassName
1214 +            = "AbstractQueuedSynchronizer$ConditionObject";
1215 +        final Thread.State expectedAcquireState = timedAcquire
1216 +            ? Thread.State.TIMED_WAITING
1217 +            : Thread.State.WAITING;
1218 +        final Thread.State expectedAwaitState = timedAwait
1219 +            ? Thread.State.TIMED_WAITING
1220 +            : Thread.State.WAITING;
1221 +        final Lock lock = new ReentrantLock(fair);
1222 +        final Condition condition = lock.newCondition();
1223 +        final AtomicBoolean conditionSatisfied = new AtomicBoolean(false);
1224 +        lock.lock();
1225 +        final Thread thread = newStartedThread((Action) () -> {
1226 +            if (timedAcquire)
1227 +                lock.tryLock(LONGER_DELAY_MS, MILLISECONDS);
1228 +            else
1229 +                lock.lock();
1230 +            while (!conditionSatisfied.get())
1231 +                if (timedAwait)
1232 +                    condition.await(LONGER_DELAY_MS, MILLISECONDS);
1233 +                else
1234 +                    condition.await();
1235 +        });
1236 +        Callable<Boolean> waitingForLock = () -> {
1237 +            String className;
1238 +            return thread.getState() == expectedAcquireState
1239 +            && (className = blockerClassName(thread)) != null
1240 +            && className.endsWith(syncClassName);
1241 +        };
1242 +        waitForThreadToEnterWaitState(thread, waitingForLock);
1243 +
1244 +        lock.unlock();
1245 +        Callable<Boolean> waitingForCondition = () -> {
1246 +            String className;
1247 +            return thread.getState() == expectedAwaitState
1248 +            && (className = blockerClassName(thread)) != null
1249 +            && className.endsWith(conditionClassName);
1250 +        };
1251 +        waitForThreadToEnterWaitState(thread, waitingForCondition);
1252 +
1253 +        // politely release the waiter
1254 +        conditionSatisfied.set(true);
1255 +        lock.lock();
1256 +        try {
1257 +            condition.signal();
1258 +        } finally { lock.unlock(); }
1259 +
1260 +        awaitTermination(thread);
1261 +    }
1262   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines