Skip to content

Commit ee21160

Browse files
authored
WIP: Ideas about different allocators
Just playing around with getting Unsafe when we can't get the Arena for allocation etc.
1 parent 8de85be commit ee21160

File tree

1 file changed

+141
-3
lines changed

1 file changed

+141
-3
lines changed

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

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414

1515
package org.swift.swiftkit;
1616

17+
import sun.misc.Unsafe;
18+
1719
import java.lang.foreign.Arena;
1820
import java.lang.foreign.MemorySegment;
1921
import java.lang.ref.Cleaner;
22+
import java.lang.reflect.Field;
2023
import java.util.Objects;
24+
import java.util.Optional;
2125
import java.util.concurrent.ThreadFactory;
2226

2327
/**
@@ -39,12 +43,16 @@
3943
*/
4044
final 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

Comments
 (0)