Skip to content

Commit acade07

Browse files
An awaiting DCountDownLatch thread can hang if the latch is destroyed
1 parent 6ce7017 commit acade07

File tree

3 files changed

+47
-2
lines changed

3 files changed

+47
-2
lines changed

src/main/java/dev/gemfire/dtype/internal/DCountDownLatchImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ public class DCountDownLatchImpl extends AbstractDType implements DCountDownLatc
1616

1717
private final DTypeCollectionsFunction AWAIT_FN = x -> {
1818
DCountDownLatchImpl latch = (DCountDownLatchImpl) x;
19-
latch.ensureUsable();
2019
while (latch.count > 0) {
20+
latch.ensureUsable();
2121
try {
2222
latch.waiters++;
23-
latch.wait();
23+
latch.wait(100);
2424
} catch (InterruptedException e) {
2525
throw new RuntimeException(e);
2626
} finally {

src/test/java/dev/gemfire/dtype/AbstractDCountDownLatchTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatNoException;
5+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
56

67
import java.time.Duration;
78
import java.util.ArrayList;
@@ -131,4 +132,18 @@ public void testCountDownConcurrency() {
131132
assertThat(ref1.getCount()).isEqualTo(latches - (iterations * 5));
132133
}
133134

135+
@Test
136+
public void testDestroyReleasesAwaitingClient() throws Exception {
137+
DCountDownLatch ref1 = getFactory().createDCountDownLatch(testName.getMethodName(), 1);
138+
139+
Future<?> future = executor.submit(() -> ref1.await());
140+
141+
DCountDownLatch ref2 = getFactory().createDCountDownLatch(testName.getMethodName(), 1);
142+
143+
ref2.destroy();
144+
145+
assertThatThrownBy(future::get).hasRootCauseInstanceOf(DTypeException.class)
146+
.withFailMessage("countdown latch is destroyed");
147+
}
148+
134149
}

src/test/java/dev/gemfire/dtype/DCountDownLatchDUnitTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
import static org.apache.geode.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER;
99
import static org.assertj.core.api.Assertions.assertThat;
1010
import static org.assertj.core.api.Assertions.assertThatNoException;
11+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1112

1213
import java.util.Properties;
1314
import java.util.concurrent.Future;
1415
import java.util.concurrent.TimeUnit;
16+
import java.util.concurrent.TimeoutException;
1517

1618
import org.junit.BeforeClass;
1719
import org.junit.ClassRule;
@@ -97,6 +99,34 @@ public void testServerRestartRetriesExistingAwait() throws Exception {
9799
ref2.countDown();
98100

99101
assertThatNoException().isThrownBy(() -> future.get(2, TimeUnit.SECONDS));
102+
assertThat(ref1.getCount()).isEqualTo(0);
103+
}
104+
105+
@Test
106+
public void testServerStopDoesStopAwaitingClient() throws Exception {
107+
String latchName = testName.getMethodName();
108+
DCountDownLatch ref1 = getFactory().createDCountDownLatch(latchName, 1);
109+
DCountDownLatch ref2 = getFactory().createDCountDownLatch(latchName, 1);
110+
Future<Void> future = executor.submit(() -> ref1.await());
111+
112+
MemberVM primary = TestUtils.getServerForKey(testName.getMethodName(), server1, server2);
113+
primary.stop();
114+
115+
// Check that the future is still waiting
116+
assertThatThrownBy(() -> future.get(10, TimeUnit.SECONDS))
117+
.isInstanceOf(TimeoutException.class);
118+
119+
ref2.countDown();
120+
121+
// Should not throw a TimeoutException and should be much shorter than the default lease
122+
// expiration timeout (60s).
123+
future.get(10, TimeUnit.SECONDS);
124+
125+
if (primary.equals(server1)) {
126+
server1 = cluster.startServerVM(1, props, locator.getPort());
127+
} else {
128+
server2 = cluster.startServerVM(2, props, locator.getPort());
129+
}
100130
}
101131

102132
}

0 commit comments

Comments
 (0)