Skip to content

Commit a0d4ec5

Browse files
committed
Fix race condition in P1.memo()
The race was when two thread encountered the uninitialized state, `a == null`. One thread gets suspended before entering the `synchronized` block, while the other runs though it. The latter thread sets `v` to something not null. Then the first thread resumes, enters the `synchronized` block and checks if `(v == null || v.get() == null)` which is now false, and thus returns the still uninitialized `a`. Fixes #105
1 parent b59c3d8 commit a0d4ec5

2 files changed

Lines changed: 36 additions & 2 deletions

File tree

core/src/main/java/fj/P1.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,12 @@ public A _1() {
223223
A a = v != null ? v.get() : null;
224224
if (a == null)
225225
synchronized (latch) {
226-
if (v == null || v.get() == null)
226+
if (v == null || v.get() == null) {
227227
a = self._1();
228-
v = new SoftReference<A>(a);
228+
v = new SoftReference<A>(a);
229+
} else {
230+
a = v.get();
231+
}
229232
}
230233
return a;
231234
}

core/src/test/java/fj/P1Test.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package fj;
2+
3+
import org.junit.Test;
4+
5+
import java.util.concurrent.ExecutorService;
6+
import java.util.concurrent.Executors;
7+
import java.util.concurrent.TimeUnit;
8+
import java.util.concurrent.atomic.AtomicInteger;
9+
10+
public final class P1Test {
11+
12+
@Test
13+
public void bug105() throws Exception {
14+
final P1<String> p1 = P.p("Foo").memo();
15+
final AtomicInteger nullCounter = new AtomicInteger();
16+
ExecutorService executorService = Executors.newCachedThreadPool();
17+
18+
for (int i = 0; i < 10000; i++) {
19+
executorService.submit(() -> {
20+
if (p1._1() == null) {
21+
nullCounter.incrementAndGet();
22+
}
23+
});
24+
}
25+
26+
executorService.shutdown();
27+
executorService.awaitTermination(10, TimeUnit.DAYS);
28+
29+
org.junit.Assert.assertEquals("Race condition in P1.memo()", 0, nullCounter.get());
30+
}
31+
}

0 commit comments

Comments
 (0)