Multiple calls to CountDownLatch.await(int) with timeout
android, countdownlatch, java, synchronization
Solution
If I understand your question correctly, this test proves that all your assumptions/requirements are true/met. (Run with JUnit and Hamcrest.) Note your code in the `runCodeUnderTest()` method, though it's interspersed with time recording and the timeouts are reduced by a factor of 10.
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat;
public class CountdownLatchTest {
static volatile boolean signal;
CountDownLatch latch = new CountDownLatch(1);
long elapsedTime;
long[] wakeupTimes = new long[2];
@Before
public void setUp() throws Exception {
signal = false;
}
@Test
public void successfulCountDownDuringFirstAwait() throws Exception {
countDownAfter(150);
runCodeUnderTest();
assertThat((double) elapsedTime, closeTo(150, 10));
assertThat(wakeupTimeSeparation(), lessThan(10));
}
@Test
public void successfulCountDownDuringSecondAwait() throws Exception {
countDownAfter(450);
runCodeUnderTest();
assertThat((double) elapsedTime, closeTo(450, 10));
assertThat((double) wakeupTimeSeparation(), closeTo(150, 10));
}
@Test
public void neverCountDown() throws Exception {
runCodeUnderTest();
assertThat((double) elapsedTime, closeTo(650, 10));
assertThat((double) wakeupTimeSeparation(), closeTo(350, 10));
}
@Test
public void countDownAfterSecondTimeout() throws Exception {
countDownAfter(1000);
runCodeUnderTest();
assertThat((double) elapsedTime, closeTo(650, 10));
assertThat((double) wakeupTimeSeparation(), closeTo(350, 10));
}
@Test
public void successfulCountDownFromSignalField() throws Exception {
countDownAfterSignal();
runCodeUnderTest();
assertThat((double) elapsedTime, closeTo(300, 10));
}
private int wakeupTimeSeparation() {
return (int) (wakeupTimes[1] - wakeupTimes[0]);
}
private void runCodeUnderTest() throws InterruptedException {
long start = System.currentTimeMillis();
latch.await(300, TimeUnit.MILLISECONDS);
wakeupTimes[0] = System.currentTimeMillis();
signal = true;
latch.await(350, TimeUnit.MILLISECONDS);
wakeupTimes[1] = System.currentTimeMillis();
elapsedTime = wakeupTimes[1] - start;
}
private void countDownAfter(final long millis) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
sleep(millis);
latch.countDown();
}
}).start();
}
private void countDownAfterSignal() {
new Thread(new Runnable() {
@Override
public void run() {
boolean trying = true;
while (trying) {
if (signal) {
latch.countDown();
trying = false;
}
sleep(5);
}
}
}).start();
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new IllegalStateException("Unexpected interrupt", e);
}
}
}
Problem
I use a CountDownLatch for waiting for a certain event from another component (running in a different thread). The following approach would fit the semantics of my software, but I'm not sure whether it works as a I expect: ``` mCountDownLatch.await(3000, TimeUnit.MILLISECONDS) otherComponent.aStaticVolatileVariable = true; mCountDownLatch.await(3500, TimeUnit.MILLISECONDS); ... <proceed with other stuff> ``` The scenario should be the following: I wait for 3 seconds, and if the latch is not counted down to 0, I notify the other component with that variable, and then I wait at most 3.5 seconds. If there is timeout again, then I don't care and will proceed with other operations. Note: I know it doesn't look like that, but the above scenario is totally reasonable and valid in my software. I did read the documentation of await(int,TimeUnit) and CountDownLatch, but I'm not a Java/Android expert, so I need confirmation. To me, all scenarios look valid: - If the first await is successful, then the other await will return immediately - If the first await times out, then the other await is still valid; therefore, if the other thread notices the static signal,the second await might return successfully - Both await calls time out (this is fine according to my software's semantics) Am I using await(...) correctly? Can a second await(...) be used in the above way even if a previous await(...) on the same object timed out?