Skip to content

Commit 61ea64a

Browse files
committed
memory: Try to work around a rare crash on Windows
I've been seeing occasional crashes in selftest on Windows related to Futexes on shared memory. My understanding is that (at least on Windows) the memory needs to be pinned/activated first, so let's fill it with zero bytes. Add extra checks to Futex32, just to be sure.
1 parent e5d82ea commit 61ea64a

2 files changed

Lines changed: 30 additions & 24 deletions

File tree

junixsocket-memory/src/main/java/org/newsclub/net/unix/memory/Futex32.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.lang.foreign.ValueLayout;
2323
import java.lang.foreign.ValueLayout.OfInt;
2424
import java.lang.invoke.VarHandle;
25+
import java.util.concurrent.atomic.AtomicBoolean;
2526

2627
import org.newsclub.net.unix.MemoryImplUtilInternal;
2728

@@ -34,50 +35,48 @@ final class Futex32 implements Futex {
3435

3536
private final MemorySegment ms;
3637
private final boolean zeroOnClose;
37-
private final long address;
38-
private volatile boolean closed = false;
38+
private final AtomicBoolean closed = new AtomicBoolean(false);
3939

40-
Futex32(MemorySegment addr, boolean zeroOnClose) throws IOException {
41-
this.address = addr.address();
42-
if ((address & 3) != 0) {
40+
Futex32(MemorySegment ms, boolean zeroOnClose) throws IOException {
41+
if (!ms.isAccessibleBy(Thread.currentThread())) {
42+
throw new IllegalStateException("Cannot access this MemorySegment from the current thread");
43+
}
44+
if ((ms.address() & 3) != 0) {
4345
throw new IOException("Not aligned");
4446
}
45-
if (addr.byteSize() != SharedMemory.FUTEX32_SEGMENT_SIZE) {
47+
if (ms.byteSize() != SharedMemory.FUTEX32_SEGMENT_SIZE) {
4648
throw new IOException("MemorySegment must be exactly 4 bytes long");
4749
}
48-
this.ms = addr;
50+
this.ms = ms;
4951
this.zeroOnClose = zeroOnClose;
5052

5153
// Make sure the 32-bit value is accessible (page-in memory)
52-
SharedMemory.UTIL.madvise(address, SharedMemory.FUTEX32_SEGMENT_SIZE,
54+
SharedMemory.UTIL.madvise(ms.address(), SharedMemory.FUTEX32_SEGMENT_SIZE,
5355
MemoryImplUtilInternal.MADV_WILLNEED, true);
5456
}
5557

5658
@Override
5759
public void close() {
58-
if (zeroOnClose && ms != null) {
59-
ms.set(OfInt.JAVA_INT, 0, 0);
60+
if (!closed.getAndSet(true)) {
61+
if (zeroOnClose && ms != null) {
62+
ms.set(OfInt.JAVA_INT, 0, 0);
63+
}
6064
}
61-
this.closed = true;
6265
}
6366

6467
@Override
6568
public boolean tryWait(int ifValue, int timeoutMillis) throws IOException {
66-
if (closed) {
67-
return false;
68-
}
69-
70-
return SharedMemory.UTIL.futexWait(address, ifValue, timeoutMillis);
69+
return SharedMemory.UTIL.futexWait(ms.address(), ifValue, timeoutMillis);
7170
}
7271

7372
@Override
7473
public boolean tryWake(boolean wakeAll) throws IOException {
75-
return SharedMemory.UTIL.futexWake(address, wakeAll);
74+
return SharedMemory.UTIL.futexWake(ms.address(), wakeAll);
7675
}
7776

7877
@Override
7978
public boolean isClosed() {
80-
return closed;
79+
return closed.get();
8180
}
8281

8382
MemorySegment getMemorySegment() {

junixsocket-memory/src/test/java/org/newsclub/net/unix/memory/SharedMemoryTest.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ public void testFutexCloseSharedMemoryFail() throws Exception {
551551
@Test
552552
public void testFutexAlignmentAndLength() throws Exception {
553553
try (SharedMemory mem = SharedMemory.createAnonymous(16)) {
554-
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE);
554+
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE).fill((byte) 0);
555555

556556
mem.futex(ms.asSlice(0, SharedMemory.FUTEX32_SEGMENT_SIZE)); // OK
557557
assertThrows(IOException.class, () -> mem.futex(ms.asSlice(1,
@@ -573,7 +573,7 @@ public void testFutexAlignmentAndLength() throws Exception {
573573
@Test
574574
public void testFutexIs32Bit() throws Exception {
575575
try (SharedMemory mem = SharedMemory.createAnonymous(16)) {
576-
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE);
576+
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE).fill((byte) 0);
577577

578578
ms.setAtIndex(OfInt.JAVA_INT, 1, 0xCAFEBABE);
579579
assertNotEquals(0, ms.get(OfLong.JAVA_LONG, 0));
@@ -606,18 +606,25 @@ public void testFutexReadOnlySegment() throws Exception {
606606

607607
@Test
608608
@ForkedVMRequirement(forkSupported = true)
609+
@SuppressWarnings("PMD.ExceptionAsFlowControl")
609610
public void testFutexSeparateVM() throws Exception {
610611
try (SharedMemory mem = SharedMemory.createAnonymous(FORKEDVM_FUTEX_SIZE)) {
611612
ForkedVM vm = new ForkedVM(FutexTestApp.class);
612613

613-
CompletableFuture<@Nullable IOException> cf = CompletableFuture.supplyAsync(() -> {
614+
CompletableFuture<@Nullable Exception> cf = CompletableFuture.supplyAsync(() -> {
614615
try {
615616
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE);
616617

617618
int waitRemaining = 5000;
618619
long now = System.currentTimeMillis();
619620

620621
try (Futex futex = mem.futex(ms.asSlice(0, 4))) {
622+
if (!futex.isInterProcess()) {
623+
throw new TestAbortedWithImportantMessageException(
624+
MessageType.TEST_ABORTED_SHORT_INFORMATIONAL,
625+
"On this system, SharedMemory Futexes cannot be shared between processes");
626+
}
627+
621628
do {
622629
if (!futex.tryWait(0, waitRemaining)) {
623630
waitRemaining -= (int) (System.currentTimeMillis() - now);
@@ -629,7 +636,7 @@ public void testFutexSeparateVM() throws Exception {
629636
}
630637
} while (true); // NOPMD
631638
}
632-
} catch (IOException e) {
639+
} catch (Exception e) {
633640
return e;
634641
}
635642
});
@@ -641,7 +648,7 @@ public void testFutexSeparateVM() throws Exception {
641648

642649
Process p = vm.fork();
643650

644-
IOException exc;
651+
Exception exc;
645652
try {
646653
exc = cf.get(5, TimeUnit.SECONDS);
647654
} catch (ExecutionException e) {
@@ -664,7 +671,7 @@ private void testFutex(boolean closeMem, boolean wakeUp) throws Exception {
664671
try (SharedMemory mem = SharedMemory.createAnonymous(16)) {
665672
MemorySegment ms = mem.asMappedMemorySegment(MapMode.READ_WRITE);
666673

667-
try (Futex futex = mem.futex(ms.asSlice(4, 4), wakeUp)) {
674+
try (Futex futex = mem.futex(ms.asSlice(4, 4).fill((byte) 0), wakeUp)) {
668675
assertTrue(futex.tryWait(0xDEADBEEF, 0)); // immediately returns; value doesn't match
669676

670677
long time = System.currentTimeMillis();

0 commit comments

Comments
 (0)