Skip to content

Commit

Permalink
Fix Hyphenator early GC of the native pointers
Browse files Browse the repository at this point in the history
The crashes encountered when a garbage collection thread cleaned pointers declared in the hyphenation function.
Those pointers were possibly still reserved by the JNA running thread while being cleaned up.

The pointers lifetime has been moved to the object level with a ThreadLocal wrapper for thread safety.
  • Loading branch information
NPavie committed Jul 26, 2024
1 parent f08eae1 commit ad66906
Showing 1 changed file with 34 additions and 16 deletions.
50 changes: 34 additions & 16 deletions src/main/java/ch/sbs/jhyphen/Hyphenator.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,25 @@ public class Hyphenator {
* The encoding of the hyphenation dictionary, e.g. ISO-8859-1 for German
*/
private final Charset charset;


private ThreadLocal<PointerByReference> repPointer = new ThreadLocal<PointerByReference>(){
@Override
protected PointerByReference initialValue() {
return new PointerByReference(Pointer.NULL);
}
};
private ThreadLocal<PointerByReference> posPointer = new ThreadLocal<PointerByReference>(){
@Override
protected PointerByReference initialValue() {
return new PointerByReference(Pointer.NULL);
}
};
private ThreadLocal<PointerByReference> cutPointer = new ThreadLocal<PointerByReference>(){
@Override
protected PointerByReference initialValue() {
return new PointerByReference(Pointer.NULL);
}
};
/**
* Default constructor
* @param dictionaryFile The path to the hyphenation dictionary file,
Expand Down Expand Up @@ -128,12 +146,12 @@ public byte[] hyphenate(String text) throws StandardHyphenationException {
int wordSize = wordBytes.length;
if (wordSize > wordHyphens.capacity())
wordHyphens = ByteBuffer.allocate(wordSize * 2);
PointerByReference repPointer = new PointerByReference(Pointer.NULL);
PointerByReference posPointer = new PointerByReference(Pointer.NULL);
PointerByReference cutPointer = new PointerByReference(Pointer.NULL);
repPointer.get().setValue(Pointer.NULL);
posPointer.get().setValue(Pointer.NULL);
cutPointer.get().setValue(Pointer.NULL);;
Hyphen.getLibrary().hnj_hyphen_hyphenate2(dictionary, wordBytes, wordSize, wordHyphens, null,
repPointer, posPointer, cutPointer);
if (repPointer.getValue() != Pointer.NULL)
repPointer.get(), posPointer.get(), cutPointer.get());
if (repPointer.get().getValue() != Pointer.NULL || posPointer.get().getValue() != Pointer.NULL || cutPointer.get().getValue() != Pointer.NULL)
throw new StandardHyphenationException("Text contains non-standard hyphenation points.");

// TODO: assert that last element of wordHyphens is not a hyphen
Expand Down Expand Up @@ -207,25 +225,25 @@ public Break hyphenate(String text, int lineLength) {
int wordSize = wordBytes.length;
if (wordSize > wordHyphens.capacity())
wordHyphens = ByteBuffer.allocate(wordSize * 2);
PointerByReference repPointer = new PointerByReference(Pointer.NULL);
PointerByReference posPointer = new PointerByReference(Pointer.NULL);
PointerByReference cutPointer = new PointerByReference(Pointer.NULL);
repPointer.get().setValue(Pointer.NULL);
posPointer.get().setValue(Pointer.NULL);
cutPointer.get().setValue(Pointer.NULL);
Hyphen.getLibrary().hnj_hyphen_hyphenate2(dictionary, wordBytes, wordSize, wordHyphens, null,
repPointer, posPointer, cutPointer);
repPointer.get(), posPointer.get(), cutPointer.get());

// TODO: assert that last element of wordHyphens is not a hyphen
String hyphenString = new String(wordHyphens.array(), 0, word.length());
String[] rep;
int[] pos;
int[] cut;
if (repPointer.getValue() != Pointer.NULL
&& posPointer.getValue() != Pointer.NULL
&& cutPointer.getValue() != Pointer.NULL) {
if (repPointer.get().getValue() != Pointer.NULL
&& posPointer.get().getValue() != Pointer.NULL
&& cutPointer.get().getValue() != Pointer.NULL) {

// will this also free the memory later or do I need to do this explicitly?
rep = repPointer.getValue().getStringArray(0L, wordSize, charset.name());
pos = posPointer.getValue().getIntArray(0, wordSize);
cut = cutPointer.getValue().getIntArray(0, wordSize); }
rep = repPointer.get().getValue().getStringArray(0L, wordSize, charset.name());
pos = posPointer.get().getValue().getIntArray(0, wordSize);
cut = cutPointer.get().getValue().getIntArray(0, wordSize); }
else {
rep = new String[wordSize];
pos = new int[wordSize];
Expand Down

0 comments on commit ad66906

Please sign in to comment.