15 |
|
|
16 |
|
import java.util.ArrayList; |
17 |
|
import java.util.List; |
18 |
+ |
import java.util.concurrent.Callable; |
19 |
+ |
import java.util.concurrent.CompletableFuture; |
20 |
|
import java.util.concurrent.CountDownLatch; |
21 |
|
import java.util.concurrent.Future; |
22 |
+ |
import java.util.concurrent.ThreadLocalRandom; |
23 |
|
import java.util.concurrent.TimeUnit; |
24 |
+ |
import java.util.concurrent.atomic.AtomicBoolean; |
25 |
|
import java.util.concurrent.locks.Lock; |
26 |
|
import java.util.concurrent.locks.StampedLock; |
27 |
|
import java.util.function.BiConsumer; |
28 |
+ |
import java.util.function.Consumer; |
29 |
|
import java.util.function.Function; |
30 |
|
|
31 |
|
import junit.framework.Test; |
80 |
|
} |
81 |
|
|
82 |
|
List<Action> lockLockers(Lock lock) { |
83 |
< |
List<Action> lockers = new ArrayList<>(); |
84 |
< |
lockers.add(() -> lock.lock()); |
85 |
< |
lockers.add(() -> lock.lockInterruptibly()); |
86 |
< |
lockers.add(() -> lock.tryLock()); |
87 |
< |
lockers.add(() -> lock.tryLock(Long.MIN_VALUE, DAYS)); |
88 |
< |
lockers.add(() -> lock.tryLock(0L, DAYS)); |
89 |
< |
lockers.add(() -> lock.tryLock(Long.MAX_VALUE, DAYS)); |
85 |
< |
return lockers; |
83 |
> |
return List.of( |
84 |
> |
() -> lock.lock(), |
85 |
> |
() -> lock.lockInterruptibly(), |
86 |
> |
() -> lock.tryLock(), |
87 |
> |
() -> lock.tryLock(Long.MIN_VALUE, DAYS), |
88 |
> |
() -> lock.tryLock(0L, DAYS), |
89 |
> |
() -> lock.tryLock(Long.MAX_VALUE, DAYS)); |
90 |
|
} |
91 |
|
|
92 |
|
List<Function<StampedLock, Long>> readLockers() { |
93 |
< |
List<Function<StampedLock, Long>> readLockers = new ArrayList<>(); |
94 |
< |
readLockers.add(sl -> sl.readLock()); |
95 |
< |
readLockers.add(sl -> sl.tryReadLock()); |
96 |
< |
readLockers.add(sl -> readLockInterruptiblyUninterrupted(sl)); |
97 |
< |
readLockers.add(sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS)); |
98 |
< |
readLockers.add(sl -> tryReadLockUninterrupted(sl, 0L, DAYS)); |
99 |
< |
readLockers.add(sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead())); |
96 |
< |
return readLockers; |
93 |
> |
return List.of( |
94 |
> |
sl -> sl.readLock(), |
95 |
> |
sl -> sl.tryReadLock(), |
96 |
> |
sl -> readLockInterruptiblyUninterrupted(sl), |
97 |
> |
sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS), |
98 |
> |
sl -> tryReadLockUninterrupted(sl, 0L, DAYS), |
99 |
> |
sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead())); |
100 |
|
} |
101 |
|
|
102 |
|
List<BiConsumer<StampedLock, Long>> readUnlockers() { |
103 |
< |
List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>(); |
104 |
< |
readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp)); |
105 |
< |
readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead())); |
106 |
< |
readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock()); |
107 |
< |
readUnlockers.add((sl, stamp) -> sl.unlock(stamp)); |
108 |
< |
readUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); |
106 |
< |
return readUnlockers; |
103 |
> |
return List.of( |
104 |
> |
(sl, stamp) -> sl.unlockRead(stamp), |
105 |
> |
(sl, stamp) -> assertTrue(sl.tryUnlockRead()), |
106 |
> |
(sl, stamp) -> sl.asReadLock().unlock(), |
107 |
> |
(sl, stamp) -> sl.unlock(stamp), |
108 |
> |
(sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); |
109 |
|
} |
110 |
|
|
111 |
|
List<Function<StampedLock, Long>> writeLockers() { |
112 |
< |
List<Function<StampedLock, Long>> writeLockers = new ArrayList<>(); |
113 |
< |
writeLockers.add(sl -> sl.writeLock()); |
114 |
< |
writeLockers.add(sl -> sl.tryWriteLock()); |
115 |
< |
writeLockers.add(sl -> writeLockInterruptiblyUninterrupted(sl)); |
116 |
< |
writeLockers.add(sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS)); |
117 |
< |
writeLockers.add(sl -> tryWriteLockUninterrupted(sl, 0L, DAYS)); |
118 |
< |
writeLockers.add(sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead())); |
117 |
< |
return writeLockers; |
112 |
> |
return List.of( |
113 |
> |
sl -> sl.writeLock(), |
114 |
> |
sl -> sl.tryWriteLock(), |
115 |
> |
sl -> writeLockInterruptiblyUninterrupted(sl), |
116 |
> |
sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS), |
117 |
> |
sl -> tryWriteLockUninterrupted(sl, 0L, DAYS), |
118 |
> |
sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead())); |
119 |
|
} |
120 |
|
|
121 |
|
List<BiConsumer<StampedLock, Long>> writeUnlockers() { |
122 |
< |
List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>(); |
123 |
< |
writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp)); |
124 |
< |
writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite())); |
125 |
< |
writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock()); |
126 |
< |
writeUnlockers.add((sl, stamp) -> sl.unlock(stamp)); |
127 |
< |
writeUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); |
127 |
< |
return writeUnlockers; |
122 |
> |
return List.of( |
123 |
> |
(sl, stamp) -> sl.unlockWrite(stamp), |
124 |
> |
(sl, stamp) -> assertTrue(sl.tryUnlockWrite()), |
125 |
> |
(sl, stamp) -> sl.asWriteLock().unlock(), |
126 |
> |
(sl, stamp) -> sl.unlock(stamp), |
127 |
> |
(sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); |
128 |
|
} |
129 |
|
|
130 |
|
/** |
1386 |
|
} |
1387 |
|
} |
1388 |
|
|
1389 |
+ |
/** |
1390 |
+ |
* Multiple threads repeatedly contend for the same lock. |
1391 |
+ |
*/ |
1392 |
+ |
public void testConcurrentAccess() throws Exception { |
1393 |
+ |
final StampedLock sl = new StampedLock(); |
1394 |
+ |
final Lock wl = sl.asWriteLock(); |
1395 |
+ |
final Lock rl = sl.asReadLock(); |
1396 |
+ |
final long testDurationMillis = expensiveTests ? 1000 : 2; |
1397 |
+ |
final int nTasks = ThreadLocalRandom.current().nextInt(1, 10); |
1398 |
+ |
final AtomicBoolean done = new AtomicBoolean(false); |
1399 |
+ |
final List<CompletableFuture> futures = new ArrayList<>(); |
1400 |
+ |
final List<Callable<Long>> stampedWriteLockers = List.of( |
1401 |
+ |
() -> sl.writeLock(), |
1402 |
+ |
() -> writeLockInterruptiblyUninterrupted(sl), |
1403 |
+ |
() -> tryWriteLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS), |
1404 |
+ |
() -> { |
1405 |
+ |
long stamp; |
1406 |
+ |
do { stamp = sl.tryConvertToWriteLock(sl.tryOptimisticRead()); } |
1407 |
+ |
while (stamp == 0L); |
1408 |
+ |
return stamp; |
1409 |
+ |
}, |
1410 |
+ |
() -> { |
1411 |
+ |
long stamp; |
1412 |
+ |
do { stamp = sl.tryWriteLock(); } while (stamp == 0L); |
1413 |
+ |
return stamp; |
1414 |
+ |
}, |
1415 |
+ |
() -> { |
1416 |
+ |
long stamp; |
1417 |
+ |
do { stamp = sl.tryWriteLock(0L, DAYS); } while (stamp == 0L); |
1418 |
+ |
return stamp; |
1419 |
+ |
}); |
1420 |
+ |
final List<Callable<Long>> stampedReadLockers = List.of( |
1421 |
+ |
() -> sl.readLock(), |
1422 |
+ |
() -> readLockInterruptiblyUninterrupted(sl), |
1423 |
+ |
() -> tryReadLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS), |
1424 |
+ |
() -> { |
1425 |
+ |
long stamp; |
1426 |
+ |
do { stamp = sl.tryConvertToReadLock(sl.tryOptimisticRead()); } |
1427 |
+ |
while (stamp == 0L); |
1428 |
+ |
return stamp; |
1429 |
+ |
}, |
1430 |
+ |
() -> { |
1431 |
+ |
long stamp; |
1432 |
+ |
do { stamp = sl.tryReadLock(); } while (stamp == 0L); |
1433 |
+ |
return stamp; |
1434 |
+ |
}, |
1435 |
+ |
() -> { |
1436 |
+ |
long stamp; |
1437 |
+ |
do { stamp = sl.tryReadLock(0L, DAYS); } while (stamp == 0L); |
1438 |
+ |
return stamp; |
1439 |
+ |
}); |
1440 |
+ |
final List<Consumer<Long>> stampedWriteUnlockers = List.of( |
1441 |
+ |
stamp -> sl.unlockWrite(stamp), |
1442 |
+ |
stamp -> sl.unlock(stamp), |
1443 |
+ |
stamp -> assertTrue(sl.tryUnlockWrite()), |
1444 |
+ |
stamp -> wl.unlock(), |
1445 |
+ |
stamp -> sl.tryConvertToOptimisticRead(stamp)); |
1446 |
+ |
final List<Consumer<Long>> stampedReadUnlockers = List.of( |
1447 |
+ |
stamp -> sl.unlockRead(stamp), |
1448 |
+ |
stamp -> sl.unlock(stamp), |
1449 |
+ |
stamp -> assertTrue(sl.tryUnlockRead()), |
1450 |
+ |
stamp -> rl.unlock(), |
1451 |
+ |
stamp -> sl.tryConvertToOptimisticRead(stamp)); |
1452 |
+ |
final Action writer = () -> { |
1453 |
+ |
// repeatedly acquires write lock |
1454 |
+ |
var locker = chooseRandomly(stampedWriteLockers); |
1455 |
+ |
var unlocker = chooseRandomly(stampedWriteUnlockers); |
1456 |
+ |
while (!done.getAcquire()) { |
1457 |
+ |
long stamp = locker.call(); |
1458 |
+ |
try { |
1459 |
+ |
assertTrue(isWriteLockStamp(stamp)); |
1460 |
+ |
assertTrue(sl.isWriteLocked()); |
1461 |
+ |
assertFalse(isReadLockStamp(stamp)); |
1462 |
+ |
assertFalse(sl.isReadLocked()); |
1463 |
+ |
assertEquals(0, sl.getReadLockCount()); |
1464 |
+ |
assertTrue(sl.validate(stamp)); |
1465 |
+ |
} finally { |
1466 |
+ |
unlocker.accept(stamp); |
1467 |
+ |
} |
1468 |
+ |
} |
1469 |
+ |
}; |
1470 |
+ |
final Action reader = () -> { |
1471 |
+ |
// repeatedly acquires read lock |
1472 |
+ |
var locker = chooseRandomly(stampedReadLockers); |
1473 |
+ |
var unlocker = chooseRandomly(stampedReadUnlockers); |
1474 |
+ |
while (!done.getAcquire()) { |
1475 |
+ |
long stamp = locker.call(); |
1476 |
+ |
try { |
1477 |
+ |
assertFalse(isWriteLockStamp(stamp)); |
1478 |
+ |
assertFalse(sl.isWriteLocked()); |
1479 |
+ |
assertTrue(isReadLockStamp(stamp)); |
1480 |
+ |
assertTrue(sl.isReadLocked()); |
1481 |
+ |
assertTrue(sl.getReadLockCount() > 0); |
1482 |
+ |
assertTrue(sl.validate(stamp)); |
1483 |
+ |
} finally { |
1484 |
+ |
unlocker.accept(stamp); |
1485 |
+ |
} |
1486 |
+ |
} |
1487 |
+ |
}; |
1488 |
+ |
for (int i = nTasks; i--> 0; ) { |
1489 |
+ |
Action task = chooseRandomly(writer, reader); |
1490 |
+ |
futures.add(CompletableFuture.runAsync(checkedRunnable(task))); |
1491 |
+ |
} |
1492 |
+ |
Thread.sleep(testDurationMillis); |
1493 |
+ |
done.setRelease(true); |
1494 |
+ |
for (var future : futures) |
1495 |
+ |
checkTimedGet(future, null); |
1496 |
+ |
} |
1497 |
+ |
|
1498 |
|
} |