Skip to content

Commit 6f26175

Browse files
GH-7: Avoid NPE when performing concurrent destroys.
1 parent c41e68a commit 6f26175

File tree

3 files changed

+25
-1
lines changed

3 files changed

+25
-1
lines changed

src/main/java/dev/gemfire/dtype/DCountDownLatch.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ public interface DCountDownLatch {
66
/**
77
* Causes the current thread to wait until the latch has counted down to zero, unless the thread
88
* is interrupted.
9+
* <p>
10+
* If another thread destroys the latch, the awaiting thread will throw a {@code DTypeException}.
911
*/
1012
void await();
1113

1214
/**
1315
* Causes the current thread to wait until the latch has counted down to zero, unless the thread
1416
* is interrupted, or the specified waiting time has elapsed.
17+
* <p>
18+
* If another thread destroys the latch, the awaiting thread will throw a {@code DTypeException}.
1519
*
1620
* @param timeout the maximum time to wait
1721
* @param unit the time unit of the timeout argument

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ public void execute(FunctionContext<Object> context) {
3232
Region<String, AbstractDType> region = ((RegionFunctionContext<?>) context).getDataSet();
3333
AbstractDType entry = region.get(name);
3434

35+
// This may be the case if multiple clients are destroying an entry concurrently.
36+
if (entry == null) {
37+
context.getResultSender().lastResult(null);
38+
return;
39+
}
40+
3541
Callable<Object> wrappingFn = () -> {
3642
Object innerResult;
3743
if (operationType == QUERY) {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.gemfire.dtype;
22

33
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatCode;
45
import static org.assertj.core.api.Assertions.assertThatNoException;
56
import static org.assertj.core.api.Assertions.assertThatThrownBy;
67

@@ -133,7 +134,7 @@ public void testCountDownConcurrency() {
133134
}
134135

135136
@Test
136-
public void testDestroyReleasesAwaitingClient() throws Exception {
137+
public void testDestroyReleasesAwaitingClient() {
137138
DCountDownLatch ref1 = getFactory().createDCountDownLatch(testName.getMethodName(), 1);
138139

139140
Future<?> future = executor.submit(() -> ref1.await());
@@ -146,4 +147,17 @@ public void testDestroyReleasesAwaitingClient() throws Exception {
146147
.withFailMessage("countdown latch is destroyed");
147148
}
148149

150+
@Test
151+
public void testDestroyFromMultipleThreads() {
152+
assertThatCode(() -> new ConcurrentLoopingThreads(2_000,
153+
i -> createDestroyLatch(),
154+
i -> createDestroyLatch()).runInLockstep())
155+
.doesNotThrowAnyException();
156+
}
157+
158+
private void createDestroyLatch() {
159+
DCountDownLatch latch = getFactory().createDCountDownLatch(testName.getMethodName(), 1);
160+
latch.destroy();
161+
}
162+
149163
}

0 commit comments

Comments
 (0)