Skip to content

Commit a7787f8

Browse files
committed
Implement SwiftValue cleanup
1 parent 6f8647d commit a7787f8

File tree

4 files changed

+75
-11
lines changed

4 files changed

+75
-11
lines changed

SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package org.swift.swiftkit;
1616

17+
import java.lang.foreign.Arena;
1718
import java.lang.foreign.MemorySegment;
1819
import java.lang.ref.Cleaner;
1920
import java.util.Objects;
@@ -38,15 +39,24 @@
3839
*/
3940
final class AutoSwiftMemorySession implements SwiftArena {
4041

42+
private final Arena arena;
4143
private final Cleaner cleaner;
4244

4345
public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) {
4446
this.cleaner = Cleaner.create(cleanerThreadFactory);
47+
this.arena = Arena.ofAuto();
4548
}
4649

4750
@Override
4851
public void register(SwiftHeapObject object) {
49-
SwiftHeapObjectCleanup cleanupAction = new SwiftHeapObjectCleanup(object.$memorySegment(), object.$swiftType());
52+
var statusDestroyedFlag = object.$statusDestroyedFlag();
53+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
54+
55+
SwiftHeapObjectCleanup cleanupAction = new SwiftHeapObjectCleanup(
56+
object.$memorySegment(),
57+
object.$swiftType(),
58+
markAsDestroyed
59+
);
5060
register(object, cleanupAction);
5161
}
5262

@@ -62,9 +72,18 @@ void register(SwiftHeapObject object, SwiftHeapObjectCleanup cleanupAction) {
6272
@Override
6373
public void register(SwiftValue value) {
6474
Objects.requireNonNull(value, "value");
75+
76+
// We're doing this dance to avoid keeping a strong reference to the value itself
77+
var statusDestroyedFlag = value.$statusDestroyedFlag();
78+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
79+
6580
MemorySegment resource = value.$memorySegment();
66-
var cleanupAction = new SwiftValueCleanup(resource);
81+
var cleanupAction = new SwiftValueCleanup(resource, value.$swiftType(), markAsDestroyed);
6782
cleaner.register(value, cleanupAction);
6883
}
6984

85+
@Override
86+
public MemorySegment allocate(long byteSize, long byteAlignment) {
87+
return arena.allocate(byteSize, byteAlignment);
88+
}
7089
}

SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
package org.swift.swiftkit;
1616

17+
import java.lang.foreign.Arena;
18+
import java.lang.foreign.MemorySegment;
1719
import java.util.LinkedList;
1820
import java.util.List;
1921
import java.util.concurrent.atomic.AtomicInteger;
@@ -26,12 +28,15 @@ final class ConfinedSwiftMemorySession implements ClosableSwiftArena {
2628
final Thread owner;
2729
final AtomicInteger state;
2830

31+
final Arena arena;
2932
final ConfinedResourceList resources;
3033

3134
public ConfinedSwiftMemorySession(Thread owner) {
3235
this.owner = owner;
3336
this.state = new AtomicInteger(ACTIVE);
3437
this.resources = new ConfinedResourceList();
38+
39+
this.arena = Arena.ofConfined();
3540
}
3641

3742
public void checkValid() throws RuntimeException {
@@ -51,24 +56,42 @@ public void close() {
5156
if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) {
5257
this.resources.runCleanup();
5358
} // else, was already closed; do nothing
59+
60+
this.arena.close();
5461
}
5562

5663
@Override
5764
public void register(SwiftHeapObject object) {
5865
checkValid();
5966

60-
var cleanup = new SwiftHeapObjectCleanup(object.$memorySegment(), object.$swiftType());
67+
var statusDestroyedFlag = object.$statusDestroyedFlag();
68+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
69+
70+
var cleanup = new SwiftHeapObjectCleanup(
71+
object.$memorySegment(), object.$swiftType(),
72+
markAsDestroyed);
6173
this.resources.add(cleanup);
6274
}
6375

6476
@Override
6577
public void register(SwiftValue value) {
6678
checkValid();
6779

68-
var cleanup = new SwiftValueCleanup(value.$memorySegment());
80+
var statusDestroyedFlag = value.$statusDestroyedFlag();
81+
Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true);
82+
83+
var cleanup = new SwiftValueCleanup(
84+
value.$memorySegment(),
85+
value.$swiftType(),
86+
markAsDestroyed);
6987
this.resources.add(cleanup);
7088
}
7189

90+
@Override
91+
public MemorySegment allocate(long byteSize, long byteAlignment) {
92+
return arena.allocate(byteSize, byteAlignment);
93+
}
94+
7295
static final class ConfinedResourceList implements SwiftResourceList {
7396
// TODO: Could use intrusive linked list to avoid one indirection here
7497
final List<SwiftInstanceCleanup> resourceCleanups = new LinkedList<>();
@@ -82,6 +105,7 @@ public void runCleanup() {
82105
for (SwiftInstanceCleanup cleanup : resourceCleanups) {
83106
cleanup.run();
84107
}
108+
resourceCleanups.clear();
85109
}
86110
}
87111
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,19 @@ public SwiftAnyType(SwiftHeapObject object) {
5656
return $LAYOUT;
5757
}
5858

59+
/**
60+
* Get the human-readable Swift type name of this type.
61+
*/
62+
public String getSwiftName() {
63+
return SwiftKit.nameOfSwiftType(memorySegment, true);
64+
}
65+
5966
@Override
6067
public String toString() {
6168
return "AnySwiftType{" +
62-
"name=" + SwiftKit.nameOfSwiftType(memorySegment, true) +
69+
"name=" + getSwiftName() +
6370
", memorySegment=" + memorySegment +
6471
'}';
6572
}
73+
6674
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,21 @@ interface SwiftInstanceCleanup extends Runnable {
3131
// non-final for testing
3232
class SwiftHeapObjectCleanup implements SwiftInstanceCleanup {
3333

34-
final MemorySegment selfPointer;
35-
final SwiftAnyType selfType;
34+
private final MemorySegment selfPointer;
35+
private final SwiftAnyType selfType;
36+
private final Runnable markAsDestroyed;
3637

3738
/**
3839
* This constructor on purpose does not just take a {@link SwiftHeapObject} in order to make it very
3940
* clear that it does not take ownership of it, but we ONLY manage the native resource here.
40-
*
41+
* </p>
4142
* This is important for {@link AutoSwiftMemorySession} which relies on the wrapper type to be GC-able,
4243
* when no longer "in use" on the Java side.
4344
*/
44-
SwiftHeapObjectCleanup(MemorySegment selfPointer, SwiftAnyType selfType) {
45+
SwiftHeapObjectCleanup(MemorySegment selfPointer,
46+
SwiftAnyType selfType, Runnable markAsDestroyed) {
4547
this.selfPointer = selfPointer;
48+
this.markAsDestroyed = markAsDestroyed;
4649
this.selfType = selfType;
4750
}
4851

@@ -54,6 +57,8 @@ public void run() throws UnexpectedRetainCountException {
5457
throw new UnexpectedRetainCountException(selfPointer, retainedCount, 1);
5558
}
5659

60+
this.markAsDestroyed.run();
61+
5762
// Destroy (and deinit) the object:
5863
SwiftValueWitnessTable.destroy(selfType, selfPointer);
5964

@@ -62,9 +67,17 @@ public void run() throws UnexpectedRetainCountException {
6267
}
6368
}
6469

65-
record SwiftValueCleanup(MemorySegment resource) implements SwiftInstanceCleanup {
70+
record SwiftValueCleanup(
71+
MemorySegment selfPointer,
72+
SwiftAnyType selfType,
73+
Runnable markAsDestroyed
74+
) implements SwiftInstanceCleanup {
75+
6676
@Override
6777
public void run() {
68-
throw new RuntimeException("not implemented yet");
78+
System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer);
79+
80+
markAsDestroyed.run();
81+
SwiftValueWitnessTable.destroy(selfType, selfPointer);
6982
}
7083
}

0 commit comments

Comments
 (0)