14
14
15
15
package org .swift .swiftkit ;
16
16
17
+ import sun .misc .Unsafe ;
18
+
17
19
import java .lang .foreign .Arena ;
18
20
import java .lang .foreign .MemorySegment ;
19
21
import java .lang .ref .Cleaner ;
22
+ import java .lang .reflect .Field ;
20
23
import java .util .Objects ;
24
+ import java .util .Optional ;
21
25
import java .util .concurrent .ThreadFactory ;
22
26
23
27
/**
39
43
*/
40
44
final class AutoSwiftMemorySession implements SwiftArena {
41
45
42
- private final Arena arena ;
46
+ private final SwiftMemoryAllocator allocator ;
43
47
private final Cleaner cleaner ;
44
48
45
49
public AutoSwiftMemorySession (ThreadFactory cleanerThreadFactory ) {
50
+ this .allocator = SwiftMemoryAllocator .getBestAvailable ();
51
+ this .cleaner = Cleaner .create (cleanerThreadFactory );
52
+ }
53
+ public AutoSwiftMemorySession (ThreadFactory cleanerThreadFactory , SwiftMemoryAllocator allocator ) {
54
+ this .allocator = allocator ;
46
55
this .cleaner = Cleaner .create (cleanerThreadFactory );
47
- this .arena = Arena .ofAuto ();
48
56
}
49
57
50
58
@ Override
@@ -62,6 +70,136 @@ public void register(SwiftInstance instance) {
62
70
63
71
@ Override
64
72
public MemorySegment allocate (long byteSize , long byteAlignment ) {
65
- return arena .allocate (byteSize , byteAlignment );
73
+ SwiftAllocation allocation = allocator .allocate (byteSize , byteAlignment );
74
+ return MemorySegment .ofAddress (allocation .address ());
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Represents a native memory allocation, regardless of mechanism used to perform the allocation.
80
+ * This memory must be manually free-d using the same allocator that was used to create it.
81
+ *
82
+ * @param address the memory address of the allocation
83
+ * @param size the size of the allocation in bytes
84
+ */
85
+ record SwiftAllocation (long address , long size ) {
86
+ }
87
+
88
+ interface SwiftMemoryAllocator {
89
+
90
+ static SwiftMemoryAllocator getBestAvailable () {
91
+ return UnsafeSwiftMemoryAllocator .get ()
92
+ .orElseThrow (() -> new IllegalStateException ("No SwiftMemoryAllocator available!" ));
93
+ }
94
+
95
+ SwiftAllocation allocate (long bytes , long byteAlignment );
96
+
97
+ /**
98
+ * Frees previously allocated memory.
99
+ *
100
+ * @param allocation the allocation returned by allocate()
101
+ */
102
+ default void free (SwiftAllocation allocation ) {
103
+ free (allocation .address ());
104
+ }
105
+
106
+ void free (long address );
107
+
108
+ void close ();
109
+ }
110
+
111
+ final class ArenaSwiftMemoryAllocator implements SwiftMemoryAllocator {
112
+
113
+ final Arena arena ;
114
+
115
+ public ArenaSwiftMemoryAllocator () {
116
+ this .arena = Arena .ofConfined ();
117
+ }
118
+
119
+ @ Override
120
+ public SwiftAllocation allocate (long bytes , long byteAlignment ) {
121
+ var segment = arena .allocate (bytes , byteAlignment );
122
+ return new SwiftAllocation (segment .address (), bytes );
123
+ }
124
+
125
+ @ Override
126
+ public void free (long address ) {
127
+
128
+ }
129
+
130
+ @ Override
131
+ public void close () {
132
+ arena .close ();
133
+ }
134
+ }
135
+
136
+ final class UnsafeSwiftMemoryAllocator implements SwiftMemoryAllocator {
137
+ private static final Unsafe unsafe ;
138
+
139
+ static {
140
+ Unsafe u = null ;
141
+ try {
142
+ Field theUnsafe = Unsafe .class .getDeclaredField ("theUnsafe" );
143
+ theUnsafe .setAccessible (true );
144
+ u = (Unsafe ) theUnsafe .get (null );
145
+ } catch (Exception e ) {
146
+ // we ignore the error because we're able to fallback to other mechanisms...
147
+ System .out .println ("[trace][swift-java] Cannot obtain Unsafe instance, will not be able to use UnsafeSwiftMemoryAllocator. Fallback to other allocator." ); // FIXME: logger infra
148
+ } finally {
149
+ unsafe = u ;
150
+ }
151
+ }
152
+
153
+ private static final Optional <SwiftMemoryAllocator > INSTANCE = Optional .of (new UnsafeSwiftMemoryAllocator ());
154
+
155
+ static Optional <SwiftMemoryAllocator > get () {
156
+ if (UnsafeSwiftMemoryAllocator .unsafe == null ) {
157
+ return Optional .empty ();
158
+ } else {
159
+ return UnsafeSwiftMemoryAllocator .INSTANCE ;
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Allocates n bytes of off-heap memory.
165
+ *
166
+ * @param bytes number of bytes to allocate
167
+ * @param byteAlignment alignment
168
+ * @return the base memory address
169
+ */
170
+ @ Override
171
+ public SwiftAllocation allocate (long bytes , long byteAlignment ) {
172
+ if (bytes <= 0 ) {
173
+ throw new IllegalArgumentException ("Bytes must be positive" );
174
+ }
175
+ var addr = unsafe .allocateMemory (bytes );
176
+ return new SwiftAllocation (addr , bytes );
177
+ }
178
+
179
+ @ Override
180
+ public void free (long address ) {
181
+ if (address == 0 ) {
182
+ throw new IllegalArgumentException ("Address cannot be zero" );
183
+ }
184
+ unsafe .freeMemory (address );
185
+ }
186
+
187
+ @ Override
188
+ public void close () {
189
+ // close should maybe assert that everything was freed?
190
+ }
191
+
192
+ /**
193
+ * Writes a byte value to the given address.
194
+ */
195
+ public void putByte (long address , byte value ) {
196
+ unsafe .putByte (address , value );
197
+ }
198
+
199
+ /**
200
+ * Reads a byte value from the given address.
201
+ */
202
+ public byte getByte (long address ) {
203
+ return unsafe .getByte (address );
66
204
}
67
205
}
0 commit comments