forked from JetBrains/pty4j
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Try to avoid a JNA invalid memory error by avoiding the Memory finalizer
That finalizer is known to prematurely free memory, in at least one case[1]. I noticed a non-reproducible "Invalid memory error" from a call to winpty_open that I speculate is a similar problem. [1] java-native-access/jna#664
- Loading branch information
Showing
2 changed files
with
174 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.pty4j.windows; | ||
|
||
import com.sun.jna.Memory; | ||
import com.sun.jna.Pointer; | ||
import com.sun.jna.platform.win32.WinNT; | ||
|
||
class ManualAllocation { | ||
|
||
// Expose the Memory class' protected malloc/free functions. The JNA Memory | ||
// class' finalizer is capable of freeing memory prematurely[1]. I saw the | ||
// winpty_open call produce an "Invalid memory access" error (only once, | ||
// non-reproducibly) when it used a PointerByReference object for its error | ||
// output parameter. Perhaps the PointerByReference object was somehow | ||
// finalized too soon? I'm skeptical, but avoiding the Memory finalizer | ||
// seems prudent. | ||
// | ||
// The Manual{Int,HANDLE}ByReferences classes in this module must be | ||
// explicitly closed. The Java 7 try-with-resources pattern works. | ||
// | ||
// [1] https://github.com/java-native-access/jna/issues/664 | ||
|
||
static class ManualMemory extends Memory { | ||
static Pointer alloc(long size) { | ||
long rawPtr = Memory.malloc(size); | ||
if (rawPtr == 0) { | ||
throw new OutOfMemoryError("Cannot allocate " + size + " bytes"); | ||
} | ||
try { | ||
Pointer ret = new Pointer(rawPtr); | ||
rawPtr = 0; | ||
return ret; | ||
} finally { | ||
if (rawPtr != 0) { | ||
Memory.free(rawPtr); | ||
} | ||
} | ||
} | ||
|
||
static void free(Pointer ptr) { | ||
Memory.free(Pointer.nativeValue(ptr)); | ||
} | ||
} | ||
|
||
static class ManualByReference implements AutoCloseable { | ||
private Pointer ptr; | ||
|
||
ManualByReference() { | ||
ptr = ManualMemory.alloc(Pointer.SIZE); | ||
} | ||
|
||
Pointer byRef() { | ||
return ptr; | ||
} | ||
|
||
@Override | ||
public void close() { | ||
ManualMemory.free(ptr); | ||
ptr = null; | ||
} | ||
} | ||
|
||
static class ManualHANDLEByReference extends ManualByReference { | ||
ManualHANDLEByReference() { | ||
this(null); | ||
} | ||
|
||
ManualHANDLEByReference(WinNT.HANDLE value) { | ||
setValue(value); | ||
} | ||
|
||
void setValue(WinNT.HANDLE value) { | ||
byRef().setPointer(0, value == null ? null : value.getPointer()); | ||
} | ||
|
||
WinNT.HANDLE getValue() { | ||
Pointer value = byRef().getPointer(0); | ||
return value == null ? null : new WinNT.HANDLE(value); | ||
} | ||
} | ||
|
||
static class ManualIntByReference extends ManualByReference { | ||
ManualIntByReference() { | ||
this(0); | ||
} | ||
|
||
ManualIntByReference(int value) { | ||
setValue(value); | ||
} | ||
|
||
void setValue(int value) { | ||
byRef().setInt(0, value); | ||
} | ||
|
||
int getValue() { | ||
return byRef().getInt(0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters