diff --git a/src/main/java/com/github/drrb/javarust/Greeting.java b/src/main/java/com/github/drrb/javarust/Greeting.java index aa3ec61..020a80d 100644 --- a/src/main/java/com/github/drrb/javarust/Greeting.java +++ b/src/main/java/com/github/drrb/javarust/Greeting.java @@ -49,6 +49,11 @@ protected List getFieldOrder() { @Override public void close() { + // Turn off "auto-synch". If it is on, JNA will automatically read all fields + // from the struct's memory and update them on the Java object. This synchronization + // occurs after every native method call. If it occurs after we drop the struct, JNA + // will try to read from the freed memory and cause a segmentation fault. + setAutoSynch(false); // Send the struct back to rust for the memory to be freed Greetings.INSTANCE.dropGreeting(this); } diff --git a/src/main/java/com/github/drrb/javarust/GreetingSet.java b/src/main/java/com/github/drrb/javarust/GreetingSet.java index 06ed511..f793131 100644 --- a/src/main/java/com/github/drrb/javarust/GreetingSet.java +++ b/src/main/java/com/github/drrb/javarust/GreetingSet.java @@ -26,18 +26,12 @@ * A struct that contains an array of structs. This is the Java representation * of the GreetingSet struct in Rust (see the Rust code). */ -public class GreetingSet extends Structure { +public class GreetingSet extends Structure implements Closeable { public static class ByReference extends GreetingSet implements Structure.ByReference { } - public static class ByValue extends GreetingSet implements Structure.ByValue, Closeable { - - @Override - public void close() { - Greetings.INSTANCE.dropGreetingSet(this); - } - + public static class ByValue extends GreetingSet implements Structure.ByValue { } /** @@ -48,7 +42,7 @@ public void close() { * * NB: We need to explicitly specify that the field is a pointer (i.e. we need * to use ByReference) because, by default, JNA assumes that struct fields - * are not pointers (i.e. the if you just say "Greeting", JNA assumes + * are not pointers (i.e. if you just say "Greeting", JNA assumes * "Greeting.ByValue" here). */ public Greeting.ByReference greetings; @@ -88,4 +82,20 @@ public List getGreetings() { protected List getFieldOrder() { return Arrays.asList("greetings", "numberOfGreetings"); } + + /** + * Send the GreetingSet back to Rust to be dropped. + * + * We do this because JNA doesn't free the memory when the object is garbage collected. + */ + @Override + public void close() { + // Turn off "auto-synch". If it is on, JNA will automatically read all fields + // from the struct's memory and update them on the Java object. This synchronization + // occurs after every native method call. If it occurs after we drop the struct, JNA + // will try to read from the freed memory and cause a segmentation fault. + setAutoSynch(false); + // Send the struct back to rust for the memory to be freed + Greetings.INSTANCE.dropGreetingSet(this); + } } diff --git a/src/main/java/com/github/drrb/javarust/Greetings.java b/src/main/java/com/github/drrb/javarust/Greetings.java index aa65fc6..c395be5 100644 --- a/src/main/java/com/github/drrb/javarust/Greetings.java +++ b/src/main/java/com/github/drrb/javarust/Greetings.java @@ -76,7 +76,7 @@ public interface Greetings extends Library { * This is the same as returning a {@link GreetingSet.ByReference}. JNA assumes * it's by reference when it's returned from a native function. */ - GreetingSet.ByValue renderGreetings(); + GreetingSet renderGreetings(); /** * Passing a callback that will be called from Rust with individual strings @@ -116,5 +116,5 @@ interface GreetingSetCallback extends Callback { /** * Free the memory used by a GreetingSet */ - void dropGreetingSet(GreetingSet.ByValue greetingSet); + void dropGreetingSet(GreetingSet greetingSet); } diff --git a/src/main/rust/com/github/drrb/javarust/lib/greetings.rs b/src/main/rust/com/github/drrb/javarust/lib/greetings.rs index ec00661..213c7b1 100644 --- a/src/main/rust/com/github/drrb/javarust/lib/greetings.rs +++ b/src/main/rust/com/github/drrb/javarust/lib/greetings.rs @@ -158,12 +158,12 @@ pub extern fn sendGreetings(callback: extern "C" fn(&GreetingSet)) { /// Example of returning a more complicated struct from Rust #[no_mangle] #[allow(non_snake_case)] -pub extern fn renderGreetings() -> GreetingSet { +pub extern fn renderGreetings() -> Box { let greetings = vec![ Greeting::new("Hello!"), Greeting::new("Hello again!") ]; - GreetingSet { + Box::new(GreetingSet { greetings: greetings.into_boxed_slice() - } + }) } #[no_mangle] @@ -175,7 +175,7 @@ pub extern fn dropGreeting(_: Box) { #[no_mangle] #[allow(non_snake_case)] -pub extern fn dropGreetingSet(_: GreetingSet) { +pub extern fn dropGreetingSet(_: Box) { // Do nothing here. Because we own the GreetingSet here and we're not // returning it, Rust will assume we don't want it anymore and clean it up. } diff --git a/src/test/java/com/github/drrb/javarust/GreetingsTest.java b/src/test/java/com/github/drrb/javarust/GreetingsTest.java index 72edb3d..0cb09e8 100644 --- a/src/test/java/com/github/drrb/javarust/GreetingsTest.java +++ b/src/test/java/com/github/drrb/javarust/GreetingsTest.java @@ -97,7 +97,7 @@ public void apply(GreetingSet.ByReference greetingSet) { }); List greetingStrings = new LinkedList<>(); - for (Greeting greeting: greetings) { + for (Greeting greeting : greetings) { greetingStrings.add(greeting.getText()); } @@ -106,7 +106,7 @@ public void apply(GreetingSet.ByReference greetingSet) { @Test public void shouldGetAStructFromRustContainingAnArrayOfStructs() { - try (GreetingSet.ByValue result = library.renderGreetings()) { + try (GreetingSet result = library.renderGreetings()) { List greetings = new LinkedList<>(); for (Greeting greeting : result.getGreetings()) { greetings.add(greeting.getText());