Skip to content

Commit 3c12d2d

Browse files
committed
avoid capturing instance in cleanup actions
1 parent 33617e4 commit 3c12d2d

File tree

10 files changed

+108
-32
lines changed

10 files changed

+108
-32
lines changed

SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void close() {
5858
public void register(SwiftInstance instance) {
5959
checkValid();
6060

61-
SwiftInstanceCleanup cleanup = new SwiftInstanceCleanup(instance);
61+
SwiftInstanceCleanup cleanup = instance.makeCleanupAction();
6262
this.resources.add(cleanup);
6363
}
6464

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.swift.swiftkitcore;
2+
3+
import java.util.concurrent.atomic.AtomicBoolean;
4+
5+
public class JNISwiftInstance extends SwiftInstance {
6+
/**
7+
* The designated constructor of any imported Swift types.
8+
*
9+
* @param pointer a pointer to the memory containing the value
10+
* @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed.
11+
*/
12+
protected JNISwiftInstance(long pointer, SwiftArena arena) {
13+
super(pointer, arena);
14+
}
15+
16+
@Override
17+
public SwiftInstanceCleanup makeCleanupAction() {
18+
final AtomicBoolean statusDestroyedFlag = $statusDestroyedFlag();
19+
Runnable markAsDestroyed = new Runnable() {
20+
@Override
21+
public void run() {
22+
statusDestroyedFlag.set(true);
23+
}
24+
};
25+
26+
return new JNISwiftInstanceCleanup(this.getClass(), this.pointer(), markAsDestroyed);
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.swift.swiftkitcore;
2+
3+
import java.lang.reflect.Method;
4+
5+
class JNISwiftInstanceCleanup implements SwiftInstanceCleanup {
6+
private final Class<? extends SwiftInstance> clazz;
7+
private final long selfPointer;
8+
private final Runnable markAsDestroyed;
9+
10+
public JNISwiftInstanceCleanup(Class<? extends SwiftInstance> clazz, long selfPointer, Runnable markAsDestroyed) {
11+
this.clazz = clazz;
12+
this.selfPointer = selfPointer;
13+
this.markAsDestroyed = markAsDestroyed;
14+
}
15+
16+
@Override
17+
public void run() {
18+
markAsDestroyed.run();
19+
20+
try {
21+
// Use reflection to look for the static destroy method on the wrapper.
22+
Method method = clazz.getDeclaredMethod("destroy", long.class);
23+
method.invoke(null, selfPointer);
24+
} catch (Exception e) {
25+
throw new RuntimeException(e);
26+
}
27+
}
28+
}

SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ public final long pointer() {
2929

3030
/**
3131
* Called when the arena has decided the value should be destroyed.
32+
* <p/>
33+
* <b>Warning:</b> The cleanup action must not capture {@code this}.
3234
*/
33-
public abstract void destroy();
35+
public abstract SwiftInstanceCleanup makeCleanupAction();
3436

3537
// TODO: make this a flagset integer and/or use a field updater
3638
/** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */

SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,4 @@
1717
/**
1818
* A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc.
1919
*/
20-
public final class SwiftInstanceCleanup implements Runnable {
21-
// TODO: Should this be a weak reference?
22-
private final SwiftInstance swiftInstance;
23-
24-
public SwiftInstanceCleanup(SwiftInstance swiftInstance) {
25-
this.swiftInstance = swiftInstance;
26-
}
27-
28-
@Override
29-
public void run() {
30-
swiftInstance.$statusDestroyedFlag().set(true);
31-
32-
// System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer);
33-
swiftInstance.destroy();
34-
}
35-
}
20+
public interface SwiftInstanceCleanup extends Runnable {}

SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ static ClosableAllocatingSwiftArena ofConfined() {
1414
return new FFMConfinedSwiftMemorySession(Thread.currentThread());
1515
}
1616

17-
// static SwiftArena ofAuto() {
18-
// ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread");
19-
// return new AutoSwiftMemorySession(cleanerThreadFactory);
20-
// }
17+
static AllocatingSwiftArena ofAuto() {
18+
ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread");
19+
return new AllocatingAutoSwiftMemorySession(cleanerThreadFactory);
20+
}
2121
}

SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@
4040
*
4141
* <p> Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}.
4242
*/
43-
final class AutoSwiftMemorySession implements AllocatingSwiftArena {
43+
final class AllocatingAutoSwiftMemorySession implements AllocatingSwiftArena {
4444

4545
private final Arena arena;
4646
private final Cleaner cleaner;
4747

48-
public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) {
48+
public AllocatingAutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) {
4949
this.cleaner = Cleaner.create(cleanerThreadFactory);
5050
this.arena = Arena.ofAuto();
5151
}
@@ -54,8 +54,9 @@ public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) {
5454
public void register(SwiftInstance instance) {
5555
Objects.requireNonNull(instance, "value");
5656

57-
// TODO: Captures warning???
58-
var cleanupAction = new SwiftInstanceCleanup(instance);
57+
// We make sure we don't capture `instance` in the
58+
// cleanup action, so we can ignore the warning below.
59+
var cleanupAction = instance.makeCleanupAction();
5960
cleaner.register(instance, cleanupAction);
6061
}
6162

SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.swift.swiftkitcore.SwiftArena;
1818
import org.swift.swiftkitcore.SwiftInstance;
19+
import org.swift.swiftkitcore.SwiftInstanceCleanup;
1920

2021
import java.lang.foreign.GroupLayout;
2122
import java.lang.foreign.MemorySegment;
@@ -45,10 +46,14 @@ protected FFMSwiftInstance(MemorySegment segment, AllocatingSwiftArena arena) {
4546
}
4647

4748
@Override
48-
public void destroy() {
49-
System.out.println("[debug] Destroy swift value [" + $swiftType().getSwiftName() + "]: " + $memorySegment());
50-
51-
// In FFM we allocate on the Java-side, so we can just call destroy.
52-
SwiftValueWitnessTable.destroy(this.$swiftType(), this.$memorySegment());
49+
public SwiftInstanceCleanup makeCleanupAction() {
50+
var statusDestroyedFlag = $statusDestroyedFlag();
51+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
52+
53+
return new FFMSwiftInstanceCleanup(
54+
$memorySegment(),
55+
$swiftType(),
56+
markAsDestroyed
57+
);
5358
}
5459
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.swift.swiftkitffm;
2+
3+
import org.swift.swiftkitcore.SwiftInstanceCleanup;
4+
5+
import java.lang.foreign.MemorySegment;
6+
7+
public class FFMSwiftInstanceCleanup implements SwiftInstanceCleanup {
8+
private final MemorySegment selfPointer;
9+
private final SwiftAnyType selfType;
10+
private final Runnable markAsDestroyed;
11+
12+
public FFMSwiftInstanceCleanup(MemorySegment selfPointer, SwiftAnyType selfType, Runnable markAsDestroyed) {
13+
this.selfPointer = selfPointer;
14+
this.selfType = selfType;
15+
this.markAsDestroyed = markAsDestroyed;
16+
}
17+
18+
@Override
19+
public void run() {
20+
markAsDestroyed.run();
21+
22+
// Allow null pointers just for AutoArena tests.
23+
if (selfType != null && selfPointer != null) {
24+
System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer);
25+
SwiftValueWitnessTable.destroy(selfType, selfPointer);
26+
}
27+
}
28+
}

SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import org.swift.swiftkitcore.SwiftHeapObject;
1818
import org.swift.swiftkitcore.util.PlatformUtils;
19-
import org.swift.swiftkitffm.util.PlatformUtils;
2019

2120
import java.lang.foreign.*;
2221
import java.lang.invoke.MethodHandle;

0 commit comments

Comments
 (0)