fix memory leak

This commit is contained in:
2026-06-02 03:26:24 +02:00
parent 06a9d5d3ec
commit 1cd908e5d0

View File

@@ -11,8 +11,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
public final class Terminal implements AutoCloseable {
private final GhosttyLibrary library;
private final MemorySegment handle;
private final Arena callbackArena = Arena.ofShared();
private final AtomicBoolean closed = new AtomicBoolean();
private Arena ptyWriterArena;
private Arena deviceAttributesArena;
private MemorySegment ptyWriterStub = MemorySegment.NULL;
private MemorySegment deviceAttributesStub = MemorySegment.NULL;
private PtyWriter ptyWriter;
@@ -40,26 +41,41 @@ public final class Terminal implements AutoCloseable {
public void setPtyWriter(PtyWriter writer) {
ensureOpen();
ptyWriter = writer;
// Each upcall stub lives in its own arena so that reassigning (or clearing) the
// writer can free the previous stub. The native pointer is repointed before the old
// arena is closed, so native never holds a dangling stub.
Arena previous = ptyWriterArena;
if (writer == null) {
ptyWriterStub = MemorySegment.NULL;
ptyWriterArena = null;
library.terminalSetPointer(handle, GhosttyLibrary.TERMINAL_OPT_WRITE_PTY, MemorySegment.NULL);
return;
} else {
Arena arena = Arena.ofShared();
ptyWriterStub = library.upcallPtyWriter(writer, arena);
ptyWriterArena = arena;
library.terminalSetPointer(handle, GhosttyLibrary.TERMINAL_OPT_WRITE_PTY, ptyWriterStub);
}
ptyWriterStub = library.upcallPtyWriter(writer, callbackArena);
library.terminalSetPointer(handle, GhosttyLibrary.TERMINAL_OPT_WRITE_PTY, ptyWriterStub);
if (previous != null) {
previous.close();
}
}
public void setDeviceAttributesProvider(DeviceAttributesProvider provider) {
ensureOpen();
deviceAttributesProvider = provider;
// See setPtyWriter: a per-stub arena lets reassignment free the previous stub.
Arena previous = deviceAttributesArena;
if (provider == null) {
deviceAttributesStub = MemorySegment.NULL;
deviceAttributesArena = null;
library.terminalSetPointer(handle, GhosttyLibrary.TERMINAL_OPT_DEVICE_ATTRIBUTES, MemorySegment.NULL);
return;
}
deviceAttributesStub = library.upcallDeviceAttributesProvider(provider, callbackArena);
} else {
Arena arena = Arena.ofShared();
deviceAttributesStub = library.upcallDeviceAttributesProvider(provider, arena);
deviceAttributesArena = arena;
library.terminalSetPointer(
handle,
GhosttyLibrary.TERMINAL_OPT_DEVICE_ATTRIBUTES,
@@ -67,6 +83,11 @@ public final class Terminal implements AutoCloseable {
);
}
if (previous != null) {
previous.close();
}
}
public String text() {
return format(TerminalFormat.PLAIN);
}
@@ -156,7 +177,14 @@ public final class Terminal implements AutoCloseable {
public void close() {
if (closed.compareAndSet(false, true)) {
library.terminalFree(handle);
callbackArena.close();
if (ptyWriterArena != null) {
ptyWriterArena.close();
ptyWriterArena = null;
}
if (deviceAttributesArena != null) {
deviceAttributesArena.close();
deviceAttributesArena = null;
}
}
}