ConditionVariable: Fix block(long) to correctly handle large timeouts
PiperOrigin-RevId: 308815613
This commit is contained in:
parent
be07b3cad5
commit
1c34029e87
@ -86,18 +86,27 @@ public class ConditionVariable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks until the condition is opened or until {@code timeout} milliseconds have passed.
|
* Blocks until the condition is opened or until {@code timeoutMs} have passed.
|
||||||
*
|
*
|
||||||
* @param timeout The maximum time to wait in milliseconds.
|
* @param timeoutMs The maximum time to wait in milliseconds. If {@code timeoutMs <= 0} then the
|
||||||
|
* call will return immediately without blocking.
|
||||||
* @return True if the condition was opened, false if the call returns because of the timeout.
|
* @return True if the condition was opened, false if the call returns because of the timeout.
|
||||||
* @throws InterruptedException If the thread is interrupted.
|
* @throws InterruptedException If the thread is interrupted.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean block(long timeout) throws InterruptedException {
|
public synchronized boolean block(long timeoutMs) throws InterruptedException {
|
||||||
long now = clock.elapsedRealtime();
|
if (timeoutMs <= 0) {
|
||||||
long end = now + timeout;
|
return isOpen;
|
||||||
while (!isOpen && now < end) {
|
}
|
||||||
wait(end - now);
|
long nowMs = clock.elapsedRealtime();
|
||||||
now = clock.elapsedRealtime();
|
long endMs = nowMs + timeoutMs;
|
||||||
|
if (endMs < nowMs) {
|
||||||
|
// timeoutMs is large enough for (nowMs + timeoutMs) to rollover. Block indefinitely.
|
||||||
|
block();
|
||||||
|
} else {
|
||||||
|
while (!isOpen && nowMs < endMs) {
|
||||||
|
wait(endMs - nowMs);
|
||||||
|
nowMs = clock.elapsedRealtime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return isOpen;
|
return isOpen;
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ public class ConditionVariableTest {
|
|||||||
public void blockWithTimeout_blocksForAtLeastTimeout() throws InterruptedException {
|
public void blockWithTimeout_blocksForAtLeastTimeout() throws InterruptedException {
|
||||||
ConditionVariable conditionVariable = buildTestConditionVariable();
|
ConditionVariable conditionVariable = buildTestConditionVariable();
|
||||||
long startTimeMs = System.currentTimeMillis();
|
long startTimeMs = System.currentTimeMillis();
|
||||||
assertThat(conditionVariable.block(/* timeout= */ 500)).isFalse();
|
assertThat(conditionVariable.block(/* timeoutMs= */ 500)).isFalse();
|
||||||
long endTimeMs = System.currentTimeMillis();
|
long endTimeMs = System.currentTimeMillis();
|
||||||
assertThat(endTimeMs - startTimeMs).isAtLeast(500);
|
assertThat(endTimeMs - startTimeMs).isAtLeast(500);
|
||||||
}
|
}
|
||||||
@ -75,6 +75,33 @@ public class ConditionVariableTest {
|
|||||||
assertThat(conditionVariable.isOpen()).isFalse();
|
assertThat(conditionVariable.isOpen()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void blockWithMaxTimeout_blocks() throws InterruptedException {
|
||||||
|
ConditionVariable conditionVariable = buildTestConditionVariable();
|
||||||
|
|
||||||
|
AtomicBoolean blockReturned = new AtomicBoolean();
|
||||||
|
AtomicBoolean blockWasInterrupted = new AtomicBoolean();
|
||||||
|
Thread blockingThread =
|
||||||
|
new Thread(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
conditionVariable.block(/* timeoutMs= */ Long.MAX_VALUE);
|
||||||
|
blockReturned.set(true);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
blockWasInterrupted.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
blockingThread.start();
|
||||||
|
Thread.sleep(500);
|
||||||
|
assertThat(blockReturned.get()).isFalse();
|
||||||
|
|
||||||
|
blockingThread.interrupt();
|
||||||
|
blockingThread.join();
|
||||||
|
assertThat(blockWasInterrupted.get()).isTrue();
|
||||||
|
assertThat(conditionVariable.isOpen()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void open_unblocksBlock() throws InterruptedException {
|
public void open_unblocksBlock() throws InterruptedException {
|
||||||
ConditionVariable conditionVariable = buildTestConditionVariable();
|
ConditionVariable conditionVariable = buildTestConditionVariable();
|
||||||
|
@ -30,7 +30,7 @@ public class TestUtilTest {
|
|||||||
public void createRobolectricConditionVariable_blockWithTimeout_timesOut()
|
public void createRobolectricConditionVariable_blockWithTimeout_timesOut()
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
|
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
|
||||||
assertThat(conditionVariable.block(/* timeout= */ 1)).isFalse();
|
assertThat(conditionVariable.block(/* timeoutMs= */ 1)).isFalse();
|
||||||
assertThat(conditionVariable.isOpen()).isFalse();
|
assertThat(conditionVariable.isOpen()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class TestUtilTest {
|
|||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
|
ConditionVariable conditionVariable = TestUtil.createRobolectricConditionVariable();
|
||||||
long startTimeMs = System.currentTimeMillis();
|
long startTimeMs = System.currentTimeMillis();
|
||||||
assertThat(conditionVariable.block(/* timeout= */ 500)).isFalse();
|
assertThat(conditionVariable.block(/* timeoutMs= */ 500)).isFalse();
|
||||||
long endTimeMs = System.currentTimeMillis();
|
long endTimeMs = System.currentTimeMillis();
|
||||||
assertThat(endTimeMs - startTimeMs).isAtLeast(500);
|
assertThat(endTimeMs - startTimeMs).isAtLeast(500);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user