From ad6690690082e3090d1b24465ba5db9343bdf676 Mon Sep 17 00:00:00 2001 From: Nicolas Pavie Date: Fri, 26 Jul 2024 10:04:01 +0200 Subject: [PATCH] Fix Hyphenator early GC of the native pointers 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. --- src/main/java/ch/sbs/jhyphen/Hyphenator.java | 50 +++++++++++++------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/main/java/ch/sbs/jhyphen/Hyphenator.java b/src/main/java/ch/sbs/jhyphen/Hyphenator.java index 24fb12a..6c9e181 100644 --- a/src/main/java/ch/sbs/jhyphen/Hyphenator.java +++ b/src/main/java/ch/sbs/jhyphen/Hyphenator.java @@ -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 repPointer = new ThreadLocal(){ + @Override + protected PointerByReference initialValue() { + return new PointerByReference(Pointer.NULL); + } + }; + private ThreadLocal posPointer = new ThreadLocal(){ + @Override + protected PointerByReference initialValue() { + return new PointerByReference(Pointer.NULL); + } + }; + private ThreadLocal cutPointer = new ThreadLocal(){ + @Override + protected PointerByReference initialValue() { + return new PointerByReference(Pointer.NULL); + } + }; /** * Default constructor * @param dictionaryFile The path to the hyphenation dictionary file, @@ -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 @@ -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];