1414
1515package org .swift .swiftkit ;
1616
17+ import sun .misc .Unsafe ;
18+
1719import java .lang .foreign .Arena ;
1820import java .lang .foreign .MemorySegment ;
1921import java .lang .ref .Cleaner ;
22+ import java .lang .reflect .Field ;
2023import java .util .Objects ;
24+ import java .util .Optional ;
2125import java .util .concurrent .ThreadFactory ;
2226
2327/**
3943 */
4044final class AutoSwiftMemorySession implements SwiftArena {
4145
42- private final Arena arena ;
46+ private final SwiftMemoryAllocator allocator ;
4347 private final Cleaner cleaner ;
4448
4549 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 ;
4655 this .cleaner = Cleaner .create (cleanerThreadFactory );
47- this .arena = Arena .ofAuto ();
4856 }
4957
5058 @ Override
@@ -62,6 +70,136 @@ public void register(SwiftInstance instance) {
6270
6371 @ Override
6472 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 );
66204 }
67205}
0 commit comments