Skip to content

Commit 794a8ca

Browse files
cpiotrcpovirk
authored andcommitted
Add Uninterruptibles methods for Condition.
Fixes #3010, #2870 RELNOTES=Add Uninterruptibles methods for Condition. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=178964181
1 parent 201af43 commit 794a8ca

File tree

4 files changed

+402
-22
lines changed

4 files changed

+402
-22
lines changed

android/guava-tests/test/com/google/common/util/concurrent/UninterruptiblesTest.java

Lines changed: 172 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.google.common.util.concurrent;
1818

1919
import static com.google.common.util.concurrent.InterruptionUtil.repeatedlyInterruptTestThread;
20+
import static com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly;
2021
import static com.google.common.util.concurrent.Uninterruptibles.joinUninterruptibly;
2122
import static com.google.common.util.concurrent.Uninterruptibles.putUninterruptibly;
2223
import static com.google.common.util.concurrent.Uninterruptibles.takeUninterruptibly;
@@ -28,10 +29,18 @@
2829
import com.google.common.testing.NullPointerTester;
2930
import com.google.common.testing.TearDown;
3031
import com.google.common.testing.TearDownStack;
32+
import java.util.Date;
3133
import java.util.concurrent.ArrayBlockingQueue;
3234
import java.util.concurrent.BlockingQueue;
3335
import java.util.concurrent.CountDownLatch;
36+
import java.util.concurrent.Executors;
37+
import java.util.concurrent.Future;
38+
import java.util.concurrent.ScheduledExecutorService;
3439
import java.util.concurrent.Semaphore;
40+
import java.util.concurrent.TimeUnit;
41+
import java.util.concurrent.locks.Condition;
42+
import java.util.concurrent.locks.Lock;
43+
import java.util.concurrent.locks.ReentrantLock;
3544
import junit.framework.TestCase;
3645

3746
/**
@@ -85,6 +94,53 @@ public void testNull() throws Exception {
8594

8695
// CountDownLatch.await() tests
8796

97+
// Condition.await() tests
98+
public void testConditionAwaitTimeoutExceeded() {
99+
Stopwatch stopwatch = Stopwatch.createStarted();
100+
Condition condition = TestCondition.create();
101+
102+
boolean signaledBeforeTimeout = awaitUninterruptibly(condition, 500, MILLISECONDS);
103+
104+
assertFalse(signaledBeforeTimeout);
105+
assertAtLeastTimePassed(stopwatch, 500);
106+
assertNotInterrupted();
107+
}
108+
109+
public void testConditionAwaitTimeoutNotExceeded() {
110+
Stopwatch stopwatch = Stopwatch.createStarted();
111+
Condition condition = TestCondition.createAndSignalAfter(500, MILLISECONDS);
112+
113+
boolean signaledBeforeTimeout = awaitUninterruptibly(condition, 1500, MILLISECONDS);
114+
115+
assertTrue(signaledBeforeTimeout);
116+
assertTimeNotPassed(stopwatch, LONG_DELAY_MS);
117+
assertNotInterrupted();
118+
}
119+
120+
public void testConditionAwaitInterruptedTimeoutExceeded() {
121+
Stopwatch stopwatch = Stopwatch.createStarted();
122+
Condition condition = TestCondition.create();
123+
requestInterruptIn(500);
124+
125+
boolean signaledBeforeTimeout = awaitUninterruptibly(condition, 1000, MILLISECONDS);
126+
127+
assertFalse(signaledBeforeTimeout);
128+
assertAtLeastTimePassed(stopwatch, 1000);
129+
assertInterrupted();
130+
}
131+
132+
public void testConditionAwaitInterruptedTimeoutNotExceeded() {
133+
Stopwatch stopwatch = Stopwatch.createStarted();
134+
Condition condition = TestCondition.createAndSignalAfter(1000, MILLISECONDS);
135+
requestInterruptIn(500);
136+
137+
boolean signaledBeforeTimeout = awaitUninterruptibly(condition, 1500, MILLISECONDS);
138+
139+
assertTrue(signaledBeforeTimeout);
140+
assertTimeNotPassed(stopwatch, LONG_DELAY_MS);
141+
assertInterrupted();
142+
}
143+
88144
// BlockingQueue.put() tests
89145
public void testPutWithNoWait() {
90146
Stopwatch stopwatch = Stopwatch.createStarted();
@@ -378,18 +434,18 @@ void assertCompletionNotExpected(long timeout) {
378434
assertAtLeastTimePassed(stopwatch, timeout);
379435
assertTimeNotPassed(stopwatch, expectedCompletionWaitMillis);
380436
}
437+
}
381438

382-
private static void assertAtLeastTimePassed(Stopwatch stopwatch, long expectedMillis) {
383-
long elapsedMillis = stopwatch.elapsed(MILLISECONDS);
384-
/*
385-
* The "+ 5" below is to permit, say, sleep(10) to sleep only 9 milliseconds. We see such
386-
* behavior sometimes when running these tests publicly as part of Guava. "+ 5" is probably
387-
* more generous than it needs to be.
388-
*/
389-
assertTrue(
390-
"Expected elapsed millis to be >= " + expectedMillis + " but was " + elapsedMillis,
391-
elapsedMillis + 5 >= expectedMillis);
392-
}
439+
private static void assertAtLeastTimePassed(Stopwatch stopwatch, long expectedMillis) {
440+
long elapsedMillis = stopwatch.elapsed(MILLISECONDS);
441+
/*
442+
* The "+ 5" below is to permit, say, sleep(10) to sleep only 9 milliseconds. We see such
443+
* behavior sometimes when running these tests publicly as part of Guava. "+ 5" is probably more
444+
* generous than it needs to be.
445+
*/
446+
assertTrue(
447+
"Expected elapsed millis to be >= " + expectedMillis + " but was " + elapsedMillis,
448+
elapsedMillis + 5 >= expectedMillis);
393449
}
394450

395451
// TODO(cpovirk): Split this into separate CountDownLatch and IncrementableCountDownLatch classes.
@@ -672,4 +728,109 @@ private static void assertNotInterrupted() {
672728
private static void requestInterruptIn(long millis) {
673729
InterruptionUtil.requestInterruptIn(millis, MILLISECONDS);
674730
}
731+
732+
private static class TestCondition implements Condition {
733+
private final Lock lock;
734+
private final Condition condition;
735+
736+
private TestCondition(Lock lock, Condition condition) {
737+
this.lock = lock;
738+
this.condition = condition;
739+
}
740+
741+
static TestCondition createAndSignalAfter(long delay, TimeUnit unit) {
742+
final TestCondition testCondition = create();
743+
744+
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1);
745+
// If signal() fails somehow, we should see a failed test, even without looking at the Future.
746+
Future<?> unused =
747+
scheduledPool.schedule(
748+
new Runnable() {
749+
@Override
750+
public void run() {
751+
testCondition.signal();
752+
}
753+
},
754+
delay,
755+
unit);
756+
757+
return testCondition;
758+
}
759+
760+
static TestCondition create() {
761+
Lock lock = new ReentrantLock();
762+
Condition condition = lock.newCondition();
763+
return new TestCondition(lock, condition);
764+
}
765+
766+
@Override
767+
public void await() throws InterruptedException {
768+
lock.lock();
769+
try {
770+
condition.await();
771+
} finally {
772+
lock.unlock();
773+
}
774+
}
775+
776+
@Override
777+
public void awaitUninterruptibly() {
778+
lock.lock();
779+
try {
780+
condition.awaitUninterruptibly();
781+
} finally {
782+
lock.unlock();
783+
}
784+
}
785+
786+
@Override
787+
public long awaitNanos(long nanosTimeout) throws InterruptedException {
788+
lock.lock();
789+
try {
790+
return condition.awaitNanos(nanosTimeout);
791+
} finally {
792+
lock.unlock();
793+
}
794+
}
795+
796+
@Override
797+
public boolean await(long time, TimeUnit unit) throws InterruptedException {
798+
lock.lock();
799+
try {
800+
return condition.await(time, unit);
801+
} finally {
802+
lock.unlock();
803+
}
804+
}
805+
806+
@Override
807+
public boolean awaitUntil(Date deadline) throws InterruptedException {
808+
lock.lock();
809+
try {
810+
return condition.awaitUntil(deadline);
811+
} finally {
812+
lock.unlock();
813+
}
814+
}
815+
816+
@Override
817+
public void signal() {
818+
lock.lock();
819+
try {
820+
condition.signal();
821+
} finally {
822+
lock.unlock();
823+
}
824+
}
825+
826+
@Override
827+
public void signalAll() {
828+
lock.lock();
829+
try {
830+
condition.signalAll();
831+
} finally {
832+
lock.unlock();
833+
}
834+
}
835+
}
675836
}

android/guava/src/com/google/common/util/concurrent/Uninterruptibles.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.concurrent.Semaphore;
3030
import java.util.concurrent.TimeUnit;
3131
import java.util.concurrent.TimeoutException;
32+
import java.util.concurrent.locks.Condition;
3233

3334
/**
3435
* Utilities for treating interruptible operations as uninterruptible. In all cases, if a thread is
@@ -93,6 +94,34 @@ public static boolean awaitUninterruptibly(CountDownLatch latch, long timeout, T
9394
}
9495
}
9596

97+
/**
98+
* Invokes {@code condition.}{@link Condition#await(long, TimeUnit) await(timeout, unit)}
99+
* uninterruptibly.
100+
*
101+
* @since NEXT
102+
*/
103+
@GwtIncompatible // concurrency
104+
public static boolean awaitUninterruptibly(Condition condition, long timeout, TimeUnit unit) {
105+
boolean interrupted = false;
106+
try {
107+
long remainingNanos = unit.toNanos(timeout);
108+
long end = System.nanoTime() + remainingNanos;
109+
110+
while (true) {
111+
try {
112+
return condition.await(remainingNanos, NANOSECONDS);
113+
} catch (InterruptedException e) {
114+
interrupted = true;
115+
remainingNanos = end - System.nanoTime();
116+
}
117+
}
118+
} finally {
119+
if (interrupted) {
120+
Thread.currentThread().interrupt();
121+
}
122+
}
123+
}
124+
96125
/** Invokes {@code toJoin.}{@link Thread#join() join()} uninterruptibly. */
97126
@GwtIncompatible // concurrency
98127
public static void joinUninterruptibly(Thread toJoin) {

0 commit comments

Comments
 (0)