Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Written by Doug Lea with assistance from members of JCP JSR-166 |
| 3 | * Expert Group and released to the public domain, as explained at |
| 4 | * http://creativecommons.org/publicdomain/zero/1.0/ |
| 5 | * Other contributors include Andrew Wright, Jeffrey Hayes, |
| 6 | * Pat Fisher, Mike Judd. |
| 7 | */ |
| 8 | |
| 9 | package jsr166; |
| 10 | |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 11 | import static java.util.concurrent.TimeUnit.MILLISECONDS; |
| 12 | |
Narayan Kamath | 8e9a0e9 | 2015-04-28 11:40:00 +0100 | [diff] [blame] | 13 | import java.util.Collection; |
| 14 | import java.util.concurrent.CountDownLatch; |
| 15 | import java.util.concurrent.Semaphore; |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 16 | |
Narayan Kamath | 8e9a0e9 | 2015-04-28 11:40:00 +0100 | [diff] [blame] | 17 | import junit.framework.AssertionFailedError; |
| 18 | import junit.framework.Test; |
| 19 | import junit.framework.TestSuite; |
| 20 | |
| 21 | public class SemaphoreTest extends JSR166TestCase { |
| 22 | // android-note: Removed because the CTS runner does a bad job of |
| 23 | // retrying tests that have suite() declarations. |
| 24 | // |
| 25 | // public static void main(String[] args) { |
| 26 | // main(suite(), args); |
| 27 | // } |
| 28 | // public static Test suite() { |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 29 | // return new TestSuite(SemaphoreTest.class); |
Narayan Kamath | 8e9a0e9 | 2015-04-28 11:40:00 +0100 | [diff] [blame] | 30 | // } |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 31 | |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 32 | /** |
| 33 | * Subclass to expose protected methods |
| 34 | */ |
| 35 | static class PublicSemaphore extends Semaphore { |
| 36 | PublicSemaphore(int permits) { super(permits); } |
| 37 | PublicSemaphore(int permits, boolean fair) { super(permits, fair); } |
| 38 | public Collection<Thread> getQueuedThreads() { |
| 39 | return super.getQueuedThreads(); |
| 40 | } |
| 41 | public boolean hasQueuedThread(Thread t) { |
| 42 | return super.getQueuedThreads().contains(t); |
| 43 | } |
| 44 | public void reducePermits(int reduction) { |
| 45 | super.reducePermits(reduction); |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * A runnable calling acquire |
| 51 | */ |
| 52 | class InterruptibleLockRunnable extends CheckedRunnable { |
| 53 | final Semaphore lock; |
| 54 | InterruptibleLockRunnable(Semaphore s) { lock = s; } |
| 55 | public void realRun() { |
| 56 | try { |
| 57 | lock.acquire(); |
| 58 | } |
| 59 | catch (InterruptedException ignored) {} |
| 60 | } |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * A runnable calling acquire that expects to be interrupted |
| 65 | */ |
| 66 | class InterruptedLockRunnable extends CheckedInterruptedRunnable { |
| 67 | final Semaphore lock; |
| 68 | InterruptedLockRunnable(Semaphore s) { lock = s; } |
| 69 | public void realRun() throws InterruptedException { |
| 70 | lock.acquire(); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | /** |
| 75 | * Spin-waits until s.hasQueuedThread(t) becomes true. |
| 76 | */ |
| 77 | void waitForQueuedThread(PublicSemaphore s, Thread t) { |
| 78 | long startTime = System.nanoTime(); |
| 79 | while (!s.hasQueuedThread(t)) { |
| 80 | if (millisElapsedSince(startTime) > LONG_DELAY_MS) |
| 81 | throw new AssertionFailedError("timed out"); |
| 82 | Thread.yield(); |
| 83 | } |
| 84 | assertTrue(s.hasQueuedThreads()); |
| 85 | assertTrue(t.isAlive()); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Spin-waits until s.hasQueuedThreads() becomes true. |
| 90 | */ |
| 91 | void waitForQueuedThreads(Semaphore s) { |
| 92 | long startTime = System.nanoTime(); |
| 93 | while (!s.hasQueuedThreads()) { |
| 94 | if (millisElapsedSince(startTime) > LONG_DELAY_MS) |
| 95 | throw new AssertionFailedError("timed out"); |
| 96 | Thread.yield(); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | enum AcquireMethod { |
| 101 | acquire() { |
| 102 | void acquire(Semaphore s) throws InterruptedException { |
| 103 | s.acquire(); |
| 104 | } |
| 105 | }, |
| 106 | acquireN() { |
| 107 | void acquire(Semaphore s, int permits) throws InterruptedException { |
| 108 | s.acquire(permits); |
| 109 | } |
| 110 | }, |
| 111 | acquireUninterruptibly() { |
| 112 | void acquire(Semaphore s) { |
| 113 | s.acquireUninterruptibly(); |
| 114 | } |
| 115 | }, |
| 116 | acquireUninterruptiblyN() { |
| 117 | void acquire(Semaphore s, int permits) { |
| 118 | s.acquireUninterruptibly(permits); |
| 119 | } |
| 120 | }, |
| 121 | tryAcquire() { |
| 122 | void acquire(Semaphore s) { |
| 123 | assertTrue(s.tryAcquire()); |
| 124 | } |
| 125 | }, |
| 126 | tryAcquireN() { |
| 127 | void acquire(Semaphore s, int permits) { |
| 128 | assertTrue(s.tryAcquire(permits)); |
| 129 | } |
| 130 | }, |
| 131 | tryAcquireTimed() { |
| 132 | void acquire(Semaphore s) throws InterruptedException { |
| 133 | assertTrue(s.tryAcquire(2 * LONG_DELAY_MS, MILLISECONDS)); |
| 134 | } |
| 135 | }, |
| 136 | tryAcquireTimedN { |
| 137 | void acquire(Semaphore s, int permits) throws InterruptedException { |
| 138 | assertTrue(s.tryAcquire(permits, 2 * LONG_DELAY_MS, MILLISECONDS)); |
| 139 | } |
| 140 | }; |
| 141 | |
| 142 | // Intentionally meta-circular |
| 143 | |
| 144 | /** Acquires 1 permit. */ |
| 145 | void acquire(Semaphore s) throws InterruptedException { |
| 146 | acquire(s, 1); |
| 147 | } |
| 148 | /** Acquires the given number of permits. */ |
| 149 | void acquire(Semaphore s, int permits) throws InterruptedException { |
| 150 | for (int i = 0; i < permits; i++) |
| 151 | acquire(s); |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * Zero, negative, and positive initial values are allowed in constructor |
| 157 | */ |
| 158 | public void testConstructor() { testConstructor(false); } |
| 159 | public void testConstructor_fair() { testConstructor(true); } |
| 160 | public void testConstructor(boolean fair) { |
| 161 | for (int permits : new int[] { -42, -1, 0, 1, 42 }) { |
| 162 | Semaphore s = new Semaphore(permits, fair); |
| 163 | assertEquals(permits, s.availablePermits()); |
| 164 | assertEquals(fair, s.isFair()); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * Constructor without fairness argument behaves as nonfair |
| 170 | */ |
| 171 | public void testConstructorDefaultsToNonFair() { |
| 172 | for (int permits : new int[] { -42, -1, 0, 1, 42 }) { |
| 173 | Semaphore s = new Semaphore(permits); |
| 174 | assertEquals(permits, s.availablePermits()); |
| 175 | assertFalse(s.isFair()); |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * tryAcquire succeeds when sufficient permits, else fails |
| 181 | */ |
| 182 | public void testTryAcquireInSameThread() { testTryAcquireInSameThread(false); } |
| 183 | public void testTryAcquireInSameThread_fair() { testTryAcquireInSameThread(true); } |
| 184 | public void testTryAcquireInSameThread(boolean fair) { |
| 185 | Semaphore s = new Semaphore(2, fair); |
| 186 | assertEquals(2, s.availablePermits()); |
| 187 | assertTrue(s.tryAcquire()); |
| 188 | assertTrue(s.tryAcquire()); |
| 189 | assertEquals(0, s.availablePermits()); |
| 190 | assertFalse(s.tryAcquire()); |
| 191 | assertFalse(s.tryAcquire()); |
| 192 | assertEquals(0, s.availablePermits()); |
| 193 | } |
| 194 | |
| 195 | /** |
| 196 | * timed tryAcquire times out |
| 197 | */ |
| 198 | public void testTryAcquire_timeout() { testTryAcquire_timeout(false); } |
| 199 | public void testTryAcquire_timeout_fair() { testTryAcquire_timeout(true); } |
| 200 | public void testTryAcquire_timeout(boolean fair) { |
| 201 | Semaphore s = new Semaphore(0, fair); |
| 202 | long startTime = System.nanoTime(); |
| 203 | try { assertFalse(s.tryAcquire(timeoutMillis(), MILLISECONDS)); } |
| 204 | catch (InterruptedException e) { threadUnexpectedException(e); } |
| 205 | assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * timed tryAcquire(N) times out |
| 210 | */ |
| 211 | public void testTryAcquireN_timeout() { testTryAcquireN_timeout(false); } |
| 212 | public void testTryAcquireN_timeout_fair() { testTryAcquireN_timeout(true); } |
| 213 | public void testTryAcquireN_timeout(boolean fair) { |
| 214 | Semaphore s = new Semaphore(2, fair); |
| 215 | long startTime = System.nanoTime(); |
| 216 | try { assertFalse(s.tryAcquire(3, timeoutMillis(), MILLISECONDS)); } |
| 217 | catch (InterruptedException e) { threadUnexpectedException(e); } |
| 218 | assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * acquire(), acquire(N), timed tryAcquired, timed tryAcquire(N) |
| 223 | * are interruptible |
| 224 | */ |
| 225 | public void testInterruptible_acquire() { testInterruptible(false, AcquireMethod.acquire); } |
| 226 | public void testInterruptible_acquire_fair() { testInterruptible(true, AcquireMethod.acquire); } |
| 227 | public void testInterruptible_acquireN() { testInterruptible(false, AcquireMethod.acquireN); } |
| 228 | public void testInterruptible_acquireN_fair() { testInterruptible(true, AcquireMethod.acquireN); } |
| 229 | public void testInterruptible_tryAcquireTimed() { testInterruptible(false, AcquireMethod.tryAcquireTimed); } |
| 230 | public void testInterruptible_tryAcquireTimed_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimed); } |
| 231 | public void testInterruptible_tryAcquireTimedN() { testInterruptible(false, AcquireMethod.tryAcquireTimedN); } |
| 232 | public void testInterruptible_tryAcquireTimedN_fair() { testInterruptible(true, AcquireMethod.tryAcquireTimedN); } |
| 233 | public void testInterruptible(boolean fair, final AcquireMethod acquirer) { |
| 234 | final PublicSemaphore s = new PublicSemaphore(0, fair); |
| 235 | final Semaphore pleaseInterrupt = new Semaphore(0, fair); |
| 236 | Thread t = newStartedThread(new CheckedRunnable() { |
| 237 | public void realRun() { |
| 238 | // Interrupt before acquire |
| 239 | Thread.currentThread().interrupt(); |
| 240 | try { |
| 241 | acquirer.acquire(s); |
| 242 | shouldThrow(); |
| 243 | } catch (InterruptedException success) {} |
| 244 | |
| 245 | // Interrupt during acquire |
| 246 | try { |
| 247 | acquirer.acquire(s); |
| 248 | shouldThrow(); |
| 249 | } catch (InterruptedException success) {} |
| 250 | |
| 251 | // Interrupt before acquire(N) |
| 252 | Thread.currentThread().interrupt(); |
| 253 | try { |
| 254 | acquirer.acquire(s, 3); |
| 255 | shouldThrow(); |
| 256 | } catch (InterruptedException success) {} |
| 257 | |
| 258 | pleaseInterrupt.release(); |
| 259 | |
| 260 | // Interrupt during acquire(N) |
| 261 | try { |
| 262 | acquirer.acquire(s, 3); |
| 263 | shouldThrow(); |
| 264 | } catch (InterruptedException success) {} |
| 265 | }}); |
| 266 | |
| 267 | waitForQueuedThread(s, t); |
| 268 | t.interrupt(); |
| 269 | await(pleaseInterrupt); |
| 270 | waitForQueuedThread(s, t); |
| 271 | t.interrupt(); |
| 272 | awaitTermination(t); |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * acquireUninterruptibly(), acquireUninterruptibly(N) are |
| 277 | * uninterruptible |
| 278 | */ |
| 279 | public void testUninterruptible_acquireUninterruptibly() { testUninterruptible(false, AcquireMethod.acquireUninterruptibly); } |
| 280 | public void testUninterruptible_acquireUninterruptibly_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptibly); } |
| 281 | public void testUninterruptible_acquireUninterruptiblyN() { testUninterruptible(false, AcquireMethod.acquireUninterruptiblyN); } |
| 282 | public void testUninterruptible_acquireUninterruptiblyN_fair() { testUninterruptible(true, AcquireMethod.acquireUninterruptiblyN); } |
| 283 | public void testUninterruptible(boolean fair, final AcquireMethod acquirer) { |
| 284 | final PublicSemaphore s = new PublicSemaphore(0, fair); |
| 285 | final Semaphore pleaseInterrupt = new Semaphore(-1, fair); |
| 286 | |
| 287 | Thread t1 = newStartedThread(new CheckedRunnable() { |
| 288 | public void realRun() throws InterruptedException { |
| 289 | // Interrupt before acquire |
| 290 | pleaseInterrupt.release(); |
| 291 | Thread.currentThread().interrupt(); |
| 292 | acquirer.acquire(s); |
| 293 | assertTrue(Thread.interrupted()); |
| 294 | }}); |
| 295 | |
| 296 | Thread t2 = newStartedThread(new CheckedRunnable() { |
| 297 | public void realRun() throws InterruptedException { |
| 298 | // Interrupt during acquire |
| 299 | pleaseInterrupt.release(); |
| 300 | acquirer.acquire(s); |
| 301 | assertTrue(Thread.interrupted()); |
| 302 | }}); |
| 303 | |
| 304 | await(pleaseInterrupt); |
| 305 | waitForQueuedThread(s, t1); |
| 306 | waitForQueuedThread(s, t2); |
| 307 | t2.interrupt(); |
| 308 | |
| 309 | assertThreadStaysAlive(t1); |
| 310 | assertTrue(t2.isAlive()); |
| 311 | |
| 312 | s.release(2); |
| 313 | |
| 314 | awaitTermination(t1); |
| 315 | awaitTermination(t2); |
| 316 | } |
| 317 | |
| 318 | /** |
| 319 | * hasQueuedThreads reports whether there are waiting threads |
| 320 | */ |
| 321 | public void testHasQueuedThreads() { testHasQueuedThreads(false); } |
| 322 | public void testHasQueuedThreads_fair() { testHasQueuedThreads(true); } |
| 323 | public void testHasQueuedThreads(boolean fair) { |
| 324 | final PublicSemaphore lock = new PublicSemaphore(1, fair); |
| 325 | assertFalse(lock.hasQueuedThreads()); |
| 326 | lock.acquireUninterruptibly(); |
| 327 | Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); |
| 328 | waitForQueuedThread(lock, t1); |
| 329 | assertTrue(lock.hasQueuedThreads()); |
| 330 | Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); |
| 331 | waitForQueuedThread(lock, t2); |
| 332 | assertTrue(lock.hasQueuedThreads()); |
| 333 | t1.interrupt(); |
| 334 | awaitTermination(t1); |
| 335 | assertTrue(lock.hasQueuedThreads()); |
| 336 | lock.release(); |
| 337 | awaitTermination(t2); |
| 338 | assertFalse(lock.hasQueuedThreads()); |
| 339 | } |
| 340 | |
| 341 | /** |
| 342 | * getQueueLength reports number of waiting threads |
| 343 | */ |
| 344 | public void testGetQueueLength() { testGetQueueLength(false); } |
| 345 | public void testGetQueueLength_fair() { testGetQueueLength(true); } |
| 346 | public void testGetQueueLength(boolean fair) { |
| 347 | final PublicSemaphore lock = new PublicSemaphore(1, fair); |
| 348 | assertEquals(0, lock.getQueueLength()); |
| 349 | lock.acquireUninterruptibly(); |
| 350 | Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); |
| 351 | waitForQueuedThread(lock, t1); |
| 352 | assertEquals(1, lock.getQueueLength()); |
| 353 | Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); |
| 354 | waitForQueuedThread(lock, t2); |
| 355 | assertEquals(2, lock.getQueueLength()); |
| 356 | t1.interrupt(); |
| 357 | awaitTermination(t1); |
| 358 | assertEquals(1, lock.getQueueLength()); |
| 359 | lock.release(); |
| 360 | awaitTermination(t2); |
| 361 | assertEquals(0, lock.getQueueLength()); |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * getQueuedThreads includes waiting threads |
| 366 | */ |
| 367 | public void testGetQueuedThreads() { testGetQueuedThreads(false); } |
| 368 | public void testGetQueuedThreads_fair() { testGetQueuedThreads(true); } |
| 369 | public void testGetQueuedThreads(boolean fair) { |
| 370 | final PublicSemaphore lock = new PublicSemaphore(1, fair); |
| 371 | assertTrue(lock.getQueuedThreads().isEmpty()); |
| 372 | lock.acquireUninterruptibly(); |
| 373 | assertTrue(lock.getQueuedThreads().isEmpty()); |
| 374 | Thread t1 = newStartedThread(new InterruptedLockRunnable(lock)); |
| 375 | waitForQueuedThread(lock, t1); |
| 376 | assertTrue(lock.getQueuedThreads().contains(t1)); |
| 377 | Thread t2 = newStartedThread(new InterruptibleLockRunnable(lock)); |
| 378 | waitForQueuedThread(lock, t2); |
| 379 | assertTrue(lock.getQueuedThreads().contains(t1)); |
| 380 | assertTrue(lock.getQueuedThreads().contains(t2)); |
| 381 | t1.interrupt(); |
| 382 | awaitTermination(t1); |
| 383 | assertFalse(lock.getQueuedThreads().contains(t1)); |
| 384 | assertTrue(lock.getQueuedThreads().contains(t2)); |
| 385 | lock.release(); |
| 386 | awaitTermination(t2); |
| 387 | assertTrue(lock.getQueuedThreads().isEmpty()); |
| 388 | } |
| 389 | |
| 390 | /** |
| 391 | * drainPermits reports and removes given number of permits |
| 392 | */ |
| 393 | public void testDrainPermits() { testDrainPermits(false); } |
| 394 | public void testDrainPermits_fair() { testDrainPermits(true); } |
| 395 | public void testDrainPermits(boolean fair) { |
| 396 | Semaphore s = new Semaphore(0, fair); |
| 397 | assertEquals(0, s.availablePermits()); |
| 398 | assertEquals(0, s.drainPermits()); |
| 399 | s.release(10); |
| 400 | assertEquals(10, s.availablePermits()); |
| 401 | assertEquals(10, s.drainPermits()); |
| 402 | assertEquals(0, s.availablePermits()); |
| 403 | assertEquals(0, s.drainPermits()); |
| 404 | } |
| 405 | |
| 406 | /** |
| 407 | * release(-N) throws IllegalArgumentException |
| 408 | */ |
| 409 | public void testReleaseIAE() { testReleaseIAE(false); } |
| 410 | public void testReleaseIAE_fair() { testReleaseIAE(true); } |
| 411 | public void testReleaseIAE(boolean fair) { |
| 412 | Semaphore s = new Semaphore(10, fair); |
| 413 | try { |
| 414 | s.release(-1); |
| 415 | shouldThrow(); |
| 416 | } catch (IllegalArgumentException success) {} |
| 417 | } |
| 418 | |
| 419 | /** |
| 420 | * reducePermits(-N) throws IllegalArgumentException |
| 421 | */ |
| 422 | public void testReducePermitsIAE() { testReducePermitsIAE(false); } |
| 423 | public void testReducePermitsIAE_fair() { testReducePermitsIAE(true); } |
| 424 | public void testReducePermitsIAE(boolean fair) { |
| 425 | PublicSemaphore s = new PublicSemaphore(10, fair); |
| 426 | try { |
| 427 | s.reducePermits(-1); |
| 428 | shouldThrow(); |
| 429 | } catch (IllegalArgumentException success) {} |
| 430 | } |
| 431 | |
| 432 | /** |
| 433 | * reducePermits reduces number of permits |
| 434 | */ |
| 435 | public void testReducePermits() { testReducePermits(false); } |
| 436 | public void testReducePermits_fair() { testReducePermits(true); } |
| 437 | public void testReducePermits(boolean fair) { |
| 438 | PublicSemaphore s = new PublicSemaphore(10, fair); |
| 439 | assertEquals(10, s.availablePermits()); |
| 440 | s.reducePermits(0); |
| 441 | assertEquals(10, s.availablePermits()); |
| 442 | s.reducePermits(1); |
| 443 | assertEquals(9, s.availablePermits()); |
| 444 | s.reducePermits(10); |
| 445 | assertEquals(-1, s.availablePermits()); |
| 446 | s.reducePermits(10); |
| 447 | assertEquals(-11, s.availablePermits()); |
| 448 | s.reducePermits(0); |
| 449 | assertEquals(-11, s.availablePermits()); |
| 450 | } |
| 451 | |
| 452 | /** |
| 453 | * a reserialized semaphore has same number of permits and |
| 454 | * fairness, but no queued threads |
| 455 | */ |
| 456 | public void testSerialization() { testSerialization(false); } |
| 457 | public void testSerialization_fair() { testSerialization(true); } |
| 458 | public void testSerialization(boolean fair) { |
| 459 | try { |
| 460 | Semaphore s = new Semaphore(3, fair); |
| 461 | s.acquire(); |
| 462 | s.acquire(); |
| 463 | s.release(); |
| 464 | |
| 465 | Semaphore clone = serialClone(s); |
| 466 | assertEquals(fair, s.isFair()); |
| 467 | assertEquals(fair, clone.isFair()); |
| 468 | assertEquals(2, s.availablePermits()); |
| 469 | assertEquals(2, clone.availablePermits()); |
| 470 | clone.acquire(); |
| 471 | clone.acquire(); |
| 472 | clone.release(); |
| 473 | assertEquals(2, s.availablePermits()); |
| 474 | assertEquals(1, clone.availablePermits()); |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 475 | assertFalse(s.hasQueuedThreads()); |
| 476 | assertFalse(clone.hasQueuedThreads()); |
| 477 | } catch (InterruptedException e) { threadUnexpectedException(e); } |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 478 | |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 479 | { |
| 480 | PublicSemaphore s = new PublicSemaphore(0, fair); |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 481 | Thread t = newStartedThread(new InterruptibleLockRunnable(s)); |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 482 | // waitForQueuedThreads(s); // suffers from "flicker", so ... |
| 483 | waitForQueuedThread(s, t); // ... we use this instead |
| 484 | PublicSemaphore clone = serialClone(s); |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 485 | assertEquals(fair, s.isFair()); |
| 486 | assertEquals(fair, clone.isFair()); |
| 487 | assertEquals(0, s.availablePermits()); |
| 488 | assertEquals(0, clone.availablePermits()); |
| 489 | assertTrue(s.hasQueuedThreads()); |
| 490 | assertFalse(clone.hasQueuedThreads()); |
| 491 | s.release(); |
| 492 | awaitTermination(t); |
| 493 | assertFalse(s.hasQueuedThreads()); |
| 494 | assertFalse(clone.hasQueuedThreads()); |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 495 | } |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 496 | } |
| 497 | |
| 498 | /** |
| 499 | * tryAcquire(n) succeeds when sufficient permits, else fails |
| 500 | */ |
| 501 | public void testTryAcquireNInSameThread() { testTryAcquireNInSameThread(false); } |
| 502 | public void testTryAcquireNInSameThread_fair() { testTryAcquireNInSameThread(true); } |
| 503 | public void testTryAcquireNInSameThread(boolean fair) { |
| 504 | Semaphore s = new Semaphore(2, fair); |
| 505 | assertEquals(2, s.availablePermits()); |
| 506 | assertFalse(s.tryAcquire(3)); |
| 507 | assertEquals(2, s.availablePermits()); |
| 508 | assertTrue(s.tryAcquire(2)); |
| 509 | assertEquals(0, s.availablePermits()); |
| 510 | assertFalse(s.tryAcquire(1)); |
| 511 | assertFalse(s.tryAcquire(2)); |
| 512 | assertEquals(0, s.availablePermits()); |
| 513 | } |
| 514 | |
| 515 | /** |
| 516 | * acquire succeeds if permits available |
| 517 | */ |
| 518 | public void testReleaseAcquireSameThread_acquire() { testReleaseAcquireSameThread(false, AcquireMethod.acquire); } |
| 519 | public void testReleaseAcquireSameThread_acquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquire); } |
| 520 | public void testReleaseAcquireSameThread_acquireN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireN); } |
| 521 | public void testReleaseAcquireSameThread_acquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireN); } |
| 522 | public void testReleaseAcquireSameThread_acquireUninterruptibly() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } |
| 523 | public void testReleaseAcquireSameThread_acquireUninterruptibly_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } |
| 524 | public void testReleaseAcquireSameThread_acquireUninterruptiblyN() { testReleaseAcquireSameThread(false, AcquireMethod.acquireUninterruptibly); } |
| 525 | public void testReleaseAcquireSameThread_acquireUninterruptiblyN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.acquireUninterruptibly); } |
| 526 | public void testReleaseAcquireSameThread_tryAcquire() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquire); } |
| 527 | public void testReleaseAcquireSameThread_tryAcquire_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquire); } |
| 528 | public void testReleaseAcquireSameThread_tryAcquireN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireN); } |
| 529 | public void testReleaseAcquireSameThread_tryAcquireN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireN); } |
| 530 | public void testReleaseAcquireSameThread_tryAcquireTimed() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimed); } |
| 531 | public void testReleaseAcquireSameThread_tryAcquireTimed_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimed); } |
| 532 | public void testReleaseAcquireSameThread_tryAcquireTimedN() { testReleaseAcquireSameThread(false, AcquireMethod.tryAcquireTimedN); } |
| 533 | public void testReleaseAcquireSameThread_tryAcquireTimedN_fair() { testReleaseAcquireSameThread(true, AcquireMethod.tryAcquireTimedN); } |
| 534 | public void testReleaseAcquireSameThread(boolean fair, |
| 535 | final AcquireMethod acquirer) { |
| 536 | Semaphore s = new Semaphore(1, fair); |
| 537 | for (int i = 1; i < 6; i++) { |
| 538 | s.release(i); |
| 539 | assertEquals(1 + i, s.availablePermits()); |
| 540 | try { |
| 541 | acquirer.acquire(s, i); |
| 542 | } catch (InterruptedException e) { threadUnexpectedException(e); } |
| 543 | assertEquals(1, s.availablePermits()); |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | /** |
| 548 | * release in one thread enables acquire in another thread |
| 549 | */ |
| 550 | public void testReleaseAcquireDifferentThreads_acquire() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquire); } |
| 551 | public void testReleaseAcquireDifferentThreads_acquire_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquire); } |
| 552 | public void testReleaseAcquireDifferentThreads_acquireN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireN); } |
| 553 | public void testReleaseAcquireDifferentThreads_acquireN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireN); } |
| 554 | public void testReleaseAcquireDifferentThreads_acquireUninterruptibly() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } |
| 555 | public void testReleaseAcquireDifferentThreads_acquireUninterruptibly_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } |
| 556 | public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.acquireUninterruptibly); } |
| 557 | public void testReleaseAcquireDifferentThreads_acquireUninterruptiblyN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.acquireUninterruptibly); } |
| 558 | public void testReleaseAcquireDifferentThreads_tryAcquireTimed() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimed); } |
| 559 | public void testReleaseAcquireDifferentThreads_tryAcquireTimed_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimed); } |
| 560 | public void testReleaseAcquireDifferentThreads_tryAcquireTimedN() { testReleaseAcquireDifferentThreads(false, AcquireMethod.tryAcquireTimedN); } |
| 561 | public void testReleaseAcquireDifferentThreads_tryAcquireTimedN_fair() { testReleaseAcquireDifferentThreads(true, AcquireMethod.tryAcquireTimedN); } |
| 562 | public void testReleaseAcquireDifferentThreads(boolean fair, |
| 563 | final AcquireMethod acquirer) { |
| 564 | final Semaphore s = new Semaphore(0, fair); |
| 565 | final int rounds = 4; |
| 566 | long startTime = System.nanoTime(); |
| 567 | Thread t = newStartedThread(new CheckedRunnable() { |
| 568 | public void realRun() throws InterruptedException { |
| 569 | for (int i = 0; i < rounds; i++) { |
| 570 | assertFalse(s.hasQueuedThreads()); |
| 571 | if (i % 2 == 0) |
| 572 | acquirer.acquire(s); |
| 573 | else |
| 574 | acquirer.acquire(s, 3); |
| 575 | }}}); |
| 576 | |
| 577 | for (int i = 0; i < rounds; i++) { |
| 578 | while (! (s.availablePermits() == 0 && s.hasQueuedThreads())) |
| 579 | Thread.yield(); |
| 580 | assertTrue(t.isAlive()); |
| 581 | if (i % 2 == 0) |
| 582 | s.release(); |
| 583 | else |
| 584 | s.release(3); |
| 585 | } |
| 586 | awaitTermination(t); |
| 587 | assertEquals(0, s.availablePermits()); |
| 588 | assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS); |
| 589 | } |
| 590 | |
| 591 | /** |
| 592 | * fair locks are strictly FIFO |
| 593 | */ |
| 594 | public void testFairLocksFifo() { |
| 595 | final PublicSemaphore s = new PublicSemaphore(1, true); |
| 596 | final CountDownLatch pleaseRelease = new CountDownLatch(1); |
| 597 | Thread t1 = newStartedThread(new CheckedRunnable() { |
| 598 | public void realRun() throws InterruptedException { |
| 599 | // Will block; permits are available, but not three |
| 600 | s.acquire(3); |
| 601 | }}); |
| 602 | |
Przemyslaw Szczepaniak | b8b7511 | 2016-03-11 15:59:10 +0000 | [diff] [blame] | 603 | waitForQueuedThread(s, t1); |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 604 | |
| 605 | Thread t2 = newStartedThread(new CheckedRunnable() { |
| 606 | public void realRun() throws InterruptedException { |
| 607 | // Will fail, even though 1 permit is available |
| 608 | assertFalse(s.tryAcquire(0L, MILLISECONDS)); |
| 609 | assertFalse(s.tryAcquire(1, 0L, MILLISECONDS)); |
| 610 | |
| 611 | // untimed tryAcquire will barge and succeed |
| 612 | assertTrue(s.tryAcquire()); |
| 613 | s.release(2); |
| 614 | assertTrue(s.tryAcquire(2)); |
| 615 | s.release(); |
| 616 | |
| 617 | pleaseRelease.countDown(); |
| 618 | // Will queue up behind t1, even though 1 permit is available |
| 619 | s.acquire(); |
| 620 | }}); |
| 621 | |
| 622 | await(pleaseRelease); |
| 623 | waitForQueuedThread(s, t2); |
| 624 | s.release(2); |
| 625 | awaitTermination(t1); |
| 626 | assertTrue(t2.isAlive()); |
| 627 | s.release(); |
| 628 | awaitTermination(t2); |
Narayan Kamath | 8e9a0e9 | 2015-04-28 11:40:00 +0100 | [diff] [blame] | 629 | } |
Calin Juravle | 8f0d92b | 2013-08-01 17:26:00 +0100 | [diff] [blame] | 630 | |
| 631 | /** |
| 632 | * toString indicates current number of permits |
| 633 | */ |
| 634 | public void testToString() { testToString(false); } |
| 635 | public void testToString_fair() { testToString(true); } |
| 636 | public void testToString(boolean fair) { |
| 637 | PublicSemaphore s = new PublicSemaphore(0, fair); |
| 638 | assertTrue(s.toString().contains("Permits = 0")); |
| 639 | s.release(); |
| 640 | assertTrue(s.toString().contains("Permits = 1")); |
| 641 | s.release(2); |
| 642 | assertTrue(s.toString().contains("Permits = 3")); |
| 643 | s.reducePermits(5); |
| 644 | assertTrue(s.toString().contains("Permits = -2")); |
| 645 | } |
| 646 | |
| 647 | } |