Skip to content

Instantly share code, notes, and snippets.

@rbuckton
Last active June 22, 2023 22:29
Show Gist options
  • Save rbuckton/49e2d4a5e2b691d393f146f8c1b8f19a to your computer and use it in GitHub Desktop.
Save rbuckton/49e2d4a5e2b691d393f146f8c1b8f19a to your computer and use it in GitHub Desktop.
SafeHandle
// a simple SafeHandle with a public `dangerousGetHandle` method.
class SafeHandle {
static #finalizer = new FinalizationRegistry(({ handle, releaseHandle }) => {
releaseHandle(handle);
});
#data = null;
constructor(handle, releaseHandle) {
this.#data = { handle, releaseHandle };
SafeHandle.#finalizer.register(this, this.#data, this);
}
get disposed() {
return this.#data === null;
}
// public, so anyone could get the handle
dangerousGetHandle() {
if (this.#data === null) throw new ReferenceError("Object is disposed");
return this.#data.handle;
}
dispose() {
if (this.#data !== null) {
SafeHandle.#finalizer.unregister(this);
const { handle, releaseHandle } = this.#data;
this.#data = null;
releaseHandle(handle);
}
}
[Symbol.dispose]() {
return this.dispose();
}
}
// usage:
function cleanupHandle(unsafeHandle) { ... }
class MyHandle extends SafeHandle {
constructor(handle) {
super(handle, cleanupHandle);
}
read() {
// dangerousGetHandle guards against disposal for you
return nativeMethods.read(this.dangerousGetHandle());
}
}
// a simple SafeHandle without a public method to access the handle.
class SafeHandle {
static #finalizer = new FinalizationRegistry(({ handle, releaseHandle }) => {
releaseHandle(handle);
});
#data = null;
constructor(handle, releaseHandle) {
this.#data = { handle, releaseHandle };
SafeHandle.#finalizer.register(this, this.#data, this);
}
get disposed() {
return this.#data === null;
}
dispose() {
if (this.#data !== null) {
SafeHandle.#finalizer.unregister(this);
const { handle, releaseHandle } = this.#data;
this.#data = null;
releaseHandle(handle);
}
}
[Symbol.dispose]() {
return this.dispose();
}
}
// usage:
function cleanupHandle(unsafeHandle) { ... }
class MyHandle extends SafeHandle {
#handle;
constructor(handle) {
super(handle, cleanupHandle);
this.#handle = handle;
}
// no public superclass method, so we use our own private method
// to guard against disposal
#dangerousGetHandle() {
if (super.disposed) throw new ReferenceError("Object disposed.");
return this.#handle;
}
read() {
return nativeMethods.read(this.#dangerousGetHandle());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment