summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2023-11-01 20:26:12 +0100
committerGitHub <noreply@github.com>2023-11-01 20:26:12 +0100
commitd42f1543121e7245789a96a485d1ef7645cb5fba (patch)
treed57a10ac527fe5b6796a3a8866af95f0f1a5d7bd /ext
parent1d19b1011bd7df50598f5981408c2d78c35b76d2 (diff)
feat: disposable Deno resources (#20845)
This commit implements Symbol.dispose and Symbol.asyncDispose for the relevant resources. Closes #20839 --------- Signed-off-by: Bartek Iwańczuk <biwanczuk@gmail.com> Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
Diffstat (limited to 'ext')
-rw-r--r--ext/fs/30_fs.js6
-rw-r--r--ext/http/00_serve.js22
-rw-r--r--ext/http/01_http.js27
-rw-r--r--ext/kv/01_db.ts5
-rw-r--r--ext/kv/lib.rs2
-rw-r--r--ext/net/01_net.js10
-rw-r--r--ext/net/lib.deno_net.d.ts6
-rw-r--r--ext/web/00_infra.js7
8 files changed, 69 insertions, 16 deletions
diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js
index d2a656a23..830703230 100644
--- a/ext/fs/30_fs.js
+++ b/ext/fs/30_fs.js
@@ -34,7 +34,7 @@ import {
ReadableStreamPrototype,
writableStreamForRid,
} from "ext:deno_web/06_streams.js";
-import { pathFromURL } from "ext:deno_web/00_infra.js";
+import { pathFromURL, SymbolDispose } from "ext:deno_web/00_infra.js";
function chmodSync(path, mode) {
ops.op_fs_chmod_sync(pathFromURL(path), mode);
@@ -669,6 +669,10 @@ class FsFile {
}
return this.#writable;
}
+
+ [SymbolDispose]() {
+ core.tryClose(this.rid);
+ }
}
function checkOpenOptions(options) {
diff --git a/ext/http/00_serve.js b/ext/http/00_serve.js
index 17a67814b..b99f28c0f 100644
--- a/ext/http/00_serve.js
+++ b/ext/http/00_serve.js
@@ -36,6 +36,7 @@ import {
} from "ext:deno_web/06_streams.js";
import { listen, listenOptionApiName, TcpConn } from "ext:deno_net/01_net.js";
import { listenTls } from "ext:deno_net/02_tls.js";
+import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js";
const {
ArrayPrototypePush,
ObjectHasOwn,
@@ -343,6 +344,7 @@ class CallbackContext {
fallbackHost;
serverRid;
closed;
+ /** @type {Promise<void> | undefined} */
closing;
listener;
@@ -671,22 +673,25 @@ function serveHttpOn(context, callback) {
PromisePrototypeCatch(callback(req), promiseErrorHandler);
}
- if (!context.closed && !context.closing) {
- context.closed = true;
- await op_http_close(rid, false);
+ if (!context.closing && !context.closed) {
+ context.closing = op_http_close(rid, false);
context.close();
}
+
+ await context.closing;
+ context.close();
+ context.closed = true;
})();
return {
finished,
async shutdown() {
- if (!context.closed && !context.closing) {
+ if (!context.closing && !context.closed) {
// Shut this HTTP server down gracefully
- context.closing = true;
- await op_http_close(context.serverRid, true);
- context.closed = true;
+ context.closing = op_http_close(context.serverRid, true);
}
+ await context.closing;
+ context.closed = true;
},
ref() {
ref = true;
@@ -700,6 +705,9 @@ function serveHttpOn(context, callback) {
core.unrefOp(currentPromise[promiseIdSymbol]);
}
},
+ [SymbolAsyncDispose]() {
+ return this.shutdown();
+ },
};
}
diff --git a/ext/http/01_http.js b/ext/http/01_http.js
index 705e616e4..cfe0f710e 100644
--- a/ext/http/01_http.js
+++ b/ext/http/01_http.js
@@ -44,6 +44,7 @@ import {
ReadableStreamPrototype,
} from "ext:deno_web/06_streams.js";
import { serve } from "ext:deno_http/00_serve.js";
+import { SymbolDispose } from "ext:deno_web/00_infra.js";
const {
ArrayPrototypeIncludes,
ArrayPrototypeMap,
@@ -69,6 +70,9 @@ const {
const connErrorSymbol = Symbol("connError");
const _deferred = Symbol("upgradeHttpDeferred");
+/** @type {(self: HttpConn, rid: number) => boolean} */
+let deleteManagedResource;
+
class HttpConn {
#rid = 0;
#closed = false;
@@ -79,7 +83,12 @@ class HttpConn {
// that were created during lifecycle of this request.
// When the connection is closed these resources should be closed
// as well.
- managedResources = new SafeSet();
+ #managedResources = new SafeSet();
+
+ static {
+ deleteManagedResource = (self, rid) =>
+ SetPrototypeDelete(self.#managedResources, rid);
+ }
constructor(rid, remoteAddr, localAddr) {
this.#rid = rid;
@@ -123,7 +132,7 @@ class HttpConn {
}
const { 0: streamRid, 1: method, 2: url } = nextRequest;
- SetPrototypeAdd(this.managedResources, streamRid);
+ SetPrototypeAdd(this.#managedResources, streamRid);
/** @type {ReadableStream<Uint8Array> | undefined} */
let body = null;
@@ -167,13 +176,21 @@ class HttpConn {
if (!this.#closed) {
this.#closed = true;
core.close(this.#rid);
- for (const rid of new SafeSetIterator(this.managedResources)) {
- SetPrototypeDelete(this.managedResources, rid);
+ for (const rid of new SafeSetIterator(this.#managedResources)) {
+ SetPrototypeDelete(this.#managedResources, rid);
core.close(rid);
}
}
}
+ [SymbolDispose]() {
+ core.tryClose(this.#rid);
+ for (const rid of new SafeSetIterator(this.#managedResources)) {
+ SetPrototypeDelete(this.#managedResources, rid);
+ core.tryClose(rid);
+ }
+ }
+
[SymbolAsyncIterator]() {
// deno-lint-ignore no-this-alias
const httpConn = this;
@@ -395,7 +412,7 @@ function createRespondWith(
abortController.abort(error);
throw error;
} finally {
- if (SetPrototypeDelete(httpConn.managedResources, streamRid)) {
+ if (deleteManagedResource(httpConn, streamRid)) {
core.close(streamRid);
}
}
diff --git a/ext/kv/01_db.ts b/ext/kv/01_db.ts
index 6e8a571f0..34678261a 100644
--- a/ext/kv/01_db.ts
+++ b/ext/kv/01_db.ts
@@ -12,6 +12,7 @@ const {
SymbolToStringTag,
Uint8ArrayPrototype,
} = globalThis.__bootstrap.primordials;
+import { SymbolDispose } from "ext:deno_web/00_infra.js";
const core = Deno.core;
const ops = core.ops;
@@ -299,6 +300,10 @@ class Kv {
close() {
core.close(this.#rid);
}
+
+ [SymbolDispose]() {
+ core.tryClose(this.#rid);
+ }
}
class AtomicOperation {
diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs
index 9e2273108..d357a2927 100644
--- a/ext/kv/lib.rs
+++ b/ext/kv/lib.rs
@@ -66,7 +66,7 @@ const MAX_TOTAL_MUTATION_SIZE_BYTES: usize = 800 * 1024;
const MAX_TOTAL_KEY_SIZE_BYTES: usize = 80 * 1024;
deno_core::extension!(deno_kv,
- deps = [ deno_console ],
+ deps = [ deno_console, deno_web ],
parameters = [ DBH: DatabaseHandler ],
ops = [
op_kv_database_open<DBH>,
diff --git a/ext/net/01_net.js b/ext/net/01_net.js
index f2bf5e7df..05aca07e5 100644
--- a/ext/net/01_net.js
+++ b/ext/net/01_net.js
@@ -9,6 +9,8 @@ import {
writableStreamForRid,
} from "ext:deno_web/06_streams.js";
import * as abortSignal from "ext:deno_web/03_abort_signal.js";
+import { SymbolDispose } from "ext:deno_web/00_infra.js";
+
const primordials = globalThis.__bootstrap.primordials;
const {
ArrayPrototypeFilter,
@@ -160,6 +162,10 @@ class Conn {
(id) => core.unrefOp(id),
);
}
+
+ [SymbolDispose]() {
+ core.tryClose(this.#rid);
+ }
}
class TcpConn extends Conn {
@@ -249,6 +255,10 @@ class Listener {
core.close(this.rid);
}
+ [SymbolDispose]() {
+ core.tryClose(this.#rid);
+ }
+
[SymbolAsyncIterator]() {
return this;
}
diff --git a/ext/net/lib.deno_net.d.ts b/ext/net/lib.deno_net.d.ts
index 030157d63..79e15563a 100644
--- a/ext/net/lib.deno_net.d.ts
+++ b/ext/net/lib.deno_net.d.ts
@@ -2,6 +2,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
+/// <reference lib="esnext.disposable" />
declare namespace Deno {
/** @category Network */
@@ -24,7 +25,8 @@ declare namespace Deno {
*
* @category Network
*/
- export interface Listener<T extends Conn = Conn> extends AsyncIterable<T> {
+ export interface Listener<T extends Conn = Conn>
+ extends AsyncIterable<T>, Disposable {
/** Waits for and resolves to the next connection to the `Listener`. */
accept(): Promise<T>;
/** Close closes the listener. Any pending accept promises will be rejected
@@ -57,7 +59,7 @@ declare namespace Deno {
export type TlsListener = Listener<TlsConn>;
/** @category Network */
- export interface Conn extends Reader, Writer, Closer {
+ export interface Conn extends Reader, Writer, Closer, Disposable {
/** The local address of the connection. */
readonly localAddr: Addr;
/** The remote address of the connection. */
diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js
index e9d5dd48b..441081604 100644
--- a/ext/web/00_infra.js
+++ b/ext/web/00_infra.js
@@ -32,6 +32,7 @@ const {
StringPrototypeSubstring,
StringPrototypeToLowerCase,
StringPrototypeToUpperCase,
+ Symbol,
TypeError,
} = primordials;
import { URLPrototype } from "ext:deno_url/00_url.js";
@@ -458,6 +459,12 @@ function pathFromURL(pathOrUrl) {
// it in unit tests
internals.pathFromURL = pathFromURL;
+// deno-lint-ignore prefer-primordials
+export const SymbolDispose = Symbol.dispose ?? Symbol("Symbol.dispose");
+// deno-lint-ignore prefer-primordials
+export const SymbolAsyncDispose = Symbol.asyncDispose ??
+ Symbol("Symbol.asyncDispose");
+
export {
ASCII_ALPHA,
ASCII_ALPHANUMERIC,