1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

perf(runtime): short-circuit queue_async_op for Poll::Ready (#15773)

This commit is contained in:
Divy Srivastava 2022-09-06 23:08:37 +05:30 committed by Yoshiya Hinosawa
parent 83dcf6ede3
commit 8a4e389bca
No known key found for this signature in database
GPG key ID: 0E8BFAA8A5B4E92B
15 changed files with 295 additions and 167 deletions

19
cli/bench/async_ops.js Normal file
View file

@ -0,0 +1,19 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
let [total, count] = typeof Deno !== "undefined"
? Deno.args
: [process.argv[2], process.argv[3]];
total = total ? parseInt(total, 0) : 50;
count = count ? parseInt(count, 10) : 100000;
async function bench(fun) {
const start = Date.now();
for (let i = 0; i < count; i++) await fun();
const elapsed = Date.now() - start;
const rate = Math.floor(count / (elapsed / 1000));
console.log(`time ${elapsed} ms rate ${rate}`);
if (--total) queueMicrotask(() => bench(fun));
}
bench(() => Deno.core.opAsync("op_void_async"));

22
cli/bench/tcp.js Normal file
View file

@ -0,0 +1,22 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
const listener = Deno.listen({ port: 4500 });
const response = new TextEncoder().encode(
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
);
// Accept a connection and write packets as fast as possible.
async function acceptWrite() {
const conn = await listener.accept();
try {
while (true) {
await conn.write(response);
}
} catch {
// Pass
}
conn.close();
}
await acceptWrite();
await acceptWrite();

View file

@ -928,7 +928,7 @@ async fn test_resolve_dns() {
let out = String::from_utf8_lossy(&output.stdout); let out = String::from_utf8_lossy(&output.stdout);
assert!(!output.status.success()); assert!(!output.status.success());
assert!(err.starts_with("Check file")); assert!(err.starts_with("Check file"));
assert!(err.contains(r#"error: Uncaught (in promise) PermissionDenied: Requires net access to "127.0.0.1:4553""#)); assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
assert!(out.is_empty()); assert!(out.is_empty());
} }
@ -950,7 +950,7 @@ async fn test_resolve_dns() {
let out = String::from_utf8_lossy(&output.stdout); let out = String::from_utf8_lossy(&output.stdout);
assert!(!output.status.success()); assert!(!output.status.success());
assert!(err.starts_with("Check file")); assert!(err.starts_with("Check file"));
assert!(err.contains(r#"error: Uncaught (in promise) PermissionDenied: Requires net access to "127.0.0.1:4553""#)); assert!(err.contains(r#"error: Uncaught PermissionDenied: Requires net access to "127.0.0.1:4553""#));
assert!(out.is_empty()); assert!(out.is_empty());
} }

View file

@ -1,2 +1,2 @@
[WILDCARD]error: Uncaught (in promise) BadResource: Bad resource ID [WILDCARD]error: Uncaught [WILDCARD] BadResource: Bad resource ID
[WILDCARD] [WILDCARD]

View file

@ -1,3 +1,3 @@
error: Uncaught (in promise) RuntimeError: unreachable error: Uncaught [WILDCARD] RuntimeError: unreachable
at <anonymous> (wasm://wasm/d1c677ea:1:41) at <anonymous> (wasm://wasm/d1c677ea:1:41)
at [WILDCARD]/wasm_unreachable.js:[WILDCARD] at [WILDCARD]/wasm_unreachable.js:[WILDCARD]

View file

@ -1202,47 +1202,44 @@ Deno.test({}, function fetchWritableRespProps() {
assertEquals(new_.headers.get("x-deno"), "foo"); assertEquals(new_.headers.get("x-deno"), "foo");
}); });
function returnHostHeaderServer(addr: string): Deno.Listener {
const [hostname, port] = addr.split(":");
const listener = Deno.listen({
hostname,
port: Number(port),
}) as Deno.Listener;
listener.accept().then(async (conn: Deno.Conn) => {
const httpConn = Deno.serveHttp(conn);
await httpConn.nextRequest()
.then(async (requestEvent: Deno.RequestEvent | null) => {
const hostHeader = requestEvent?.request.headers.get("Host");
const headersToReturn = hostHeader ? { "Host": hostHeader } : undefined;
await requestEvent?.respondWith(
new Response("", {
status: 200,
headers: headersToReturn,
}),
);
});
httpConn.close();
});
return listener;
}
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function fetchFilterOutCustomHostHeader(): Promise< async function fetchFilterOutCustomHostHeader(): Promise<
void void
> { > {
const addr = "127.0.0.1:4511"; const addr = "127.0.0.1:4511";
const listener = returnHostHeaderServer(addr); const [hostname, port] = addr.split(":");
const listener = Deno.listen({
hostname,
port: Number(port),
}) as Deno.Listener;
let httpConn: Deno.HttpConn;
listener.accept().then(async (conn: Deno.Conn) => {
httpConn = Deno.serveHttp(conn);
await httpConn.nextRequest()
.then(async (requestEvent: Deno.RequestEvent | null) => {
const hostHeader = requestEvent?.request.headers.get("Host");
const headersToReturn = hostHeader
? { "Host": hostHeader }
: undefined;
await requestEvent?.respondWith(
new Response("", {
status: 200,
headers: headersToReturn,
}),
);
});
});
const response = await fetch(`http://${addr}/`, { const response = await fetch(`http://${addr}/`, {
headers: { "Host": "example.com" }, headers: { "Host": "example.com" },
}); });
await response.text(); await response.text();
listener.close(); listener.close();
httpConn!.close();
assertEquals(response.headers.get("Host"), addr); assertEquals(response.headers.get("Host"), addr);
}, },

View file

@ -45,11 +45,12 @@ async function writeRequestAndReadResponse(conn: Deno.Conn): Promise<string> {
} }
Deno.test({ permissions: { net: true } }, async function httpServerBasic() { Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
let httpConn: Deno.HttpConn;
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest(); const reqEvent = await httpConn.nextRequest();
assert(reqEvent); assert(reqEvent);
const { request, respondWith } = reqEvent; const { request, respondWith } = reqEvent;
@ -58,7 +59,6 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
await respondWith( await respondWith(
new Response("Hello World", { headers: { "foo": "bar" } }), new Response("Hello World", { headers: { "foo": "bar" } }),
); );
httpConn.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4501/", { const resp = await fetch("http://127.0.0.1:4501/", {
@ -71,6 +71,8 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
const cloneText = await clone.text(); const cloneText = await clone.text();
assertEquals(cloneText, "Hello World"); assertEquals(cloneText, "Hello World");
await promise; await promise;
httpConn!.close();
}); });
// https://github.com/denoland/deno/issues/15107 // https://github.com/denoland/deno/issues/15107
@ -88,7 +90,7 @@ Deno.test(
const { request } = e; const { request } = e;
request.text(); request.text();
headers = request.headers; headers = request.headers;
httpConn.close(); httpConn!.close();
})(); })();
const conn = await Deno.connect({ port: 2333 }); const conn = await Deno.connect({ port: 2333 });
@ -120,7 +122,7 @@ Deno.test(
await respondWith(new Response("Hello World")); // Closes request await respondWith(new Response("Hello World")); // Closes request
assertThrows(() => request.headers, TypeError, "request closed"); assertThrows(() => request.headers, TypeError, "request closed");
httpConn.close(); httpConn!.close();
})(); })();
const conn = await Deno.connect({ port: 2334 }); const conn = await Deno.connect({ port: 2334 });
@ -138,17 +140,17 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerGetRequestBody() { async function httpServerGetRequestBody() {
let httpConn: Deno.HttpConn;
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
assertEquals(request.body, null); assertEquals(request.body, null);
await respondWith(new Response("", { headers: {} })); await respondWith(new Response("", { headers: {} }));
httpConn.close();
})(); })();
const conn = await Deno.connect({ port: 4501 }); const conn = await Deno.connect({ port: 4501 });
@ -166,6 +168,7 @@ Deno.test(
conn.close(); conn.close();
await promise; await promise;
httpConn!.close();
}, },
); );
@ -178,23 +181,24 @@ Deno.test(
writer.write(new TextEncoder().encode("world")); writer.write(new TextEncoder().encode("world"));
writer.close(); writer.close();
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const evt = await httpConn.nextRequest(); const evt = await httpConn.nextRequest();
assert(evt); assert(evt);
const { request, respondWith } = evt; const { request, respondWith } = evt;
assert(!request.body); assert(!request.body);
await respondWith(new Response(stream.readable)); await respondWith(new Response(stream.readable));
httpConn.close();
listener.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4501/"); const resp = await fetch("http://127.0.0.1:4501/");
const respBody = await resp.text(); const respBody = await resp.text();
assertEquals("hello world", respBody); assertEquals("hello world", respBody);
await promise; await promise;
httpConn!.close();
listener.close();
}, },
); );
@ -240,17 +244,16 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerStreamDuplex() { async function httpServerStreamDuplex() {
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const evt = await httpConn.nextRequest(); const evt = await httpConn.nextRequest();
assert(evt); assert(evt);
const { request, respondWith } = evt; const { request, respondWith } = evt;
assert(request.body); assert(request.body);
await respondWith(new Response(request.body)); await respondWith(new Response(request.body));
httpConn.close();
listener.close();
})(); })();
const ts = new TransformStream(); const ts = new TransformStream();
@ -269,10 +272,13 @@ Deno.test(
const chunk2 = await reader.read(); const chunk2 = await reader.read();
assert(!chunk2.done); assert(!chunk2.done);
assertEquals(chunk2.value, new Uint8Array([2])); assertEquals(chunk2.value, new Uint8Array([2]));
await writable.close(); await writable.close();
const chunk3 = await reader.read(); const chunk3 = await reader.read();
assert(chunk3.done); assert(chunk3.done);
await promise; await promise;
httpConn!.close();
listener.close();
}, },
); );
@ -351,18 +357,17 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerRegressionHang() { async function httpServerRegressionHang() {
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const event = await httpConn.nextRequest(); const event = await httpConn.nextRequest();
assert(event); assert(event);
const { request, respondWith } = event; const { request, respondWith } = event;
const reqBody = await request.text(); const reqBody = await request.text();
assertEquals("request", reqBody); assertEquals("request", reqBody);
await respondWith(new Response("response")); await respondWith(new Response("response"));
httpConn.close();
listener.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4501/", { const resp = await fetch("http://127.0.0.1:4501/", {
@ -372,6 +377,9 @@ Deno.test(
const respBody = await resp.text(); const respBody = await resp.text();
assertEquals("response", respBody); assertEquals("response", respBody);
await promise; await promise;
httpConn!.close();
listener.close();
}, },
); );
@ -410,7 +418,7 @@ Deno.test(
cancelReason!, cancelReason!,
); );
assert(cancelReason!); assert(cancelReason!);
httpConn.close(); httpConn!.close();
listener.close(); listener.close();
})(); })();
@ -468,22 +476,23 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerEmptyBlobResponse() { async function httpServerEmptyBlobResponse() {
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const event = await httpConn.nextRequest(); const event = await httpConn.nextRequest();
assert(event); assert(event);
const { respondWith } = event; const { respondWith } = event;
await respondWith(new Response(new Blob([]))); await respondWith(new Response(new Blob([])));
httpConn.close();
listener.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4501/"); const resp = await fetch("http://127.0.0.1:4501/");
const respBody = await resp.text(); const respBody = await resp.text();
assertEquals("", respBody); assertEquals("", respBody);
await promise; await promise;
httpConn!.close();
listener.close();
}, },
); );
@ -633,14 +642,14 @@ Deno.test(
}).pipeThrough(new TextEncoderStream()); }).pipeThrough(new TextEncoderStream());
} }
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
const finished = (async () => { const finished = (async () => {
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const requestEvent = await httpConn.nextRequest(); const requestEvent = await httpConn.nextRequest();
const { respondWith } = requestEvent!; const { respondWith } = requestEvent!;
await respondWith(new Response(periodicStream())); await respondWith(new Response(periodicStream()));
httpConn.close();
})(); })();
// start a client // start a client
@ -651,6 +660,8 @@ Deno.test(
await finished; await finished;
clientConn.close(); clientConn.close();
httpConn!.close();
listener.close(); listener.close();
}, },
); );
@ -658,11 +669,12 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpRequestLatin1Headers() { async function httpRequestLatin1Headers() {
let httpConn: Deno.HttpConn;
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest(); const reqEvent = await httpConn.nextRequest();
assert(reqEvent); assert(reqEvent);
const { request, respondWith } = reqEvent; const { request, respondWith } = reqEvent;
@ -670,7 +682,6 @@ Deno.test(
await respondWith( await respondWith(
new Response("", { headers: { "X-Header-Test": "Æ" } }), new Response("", { headers: { "X-Header-Test": "Æ" } }),
); );
httpConn.close();
})(); })();
const clientConn = await Deno.connect({ port: 4501 }); const clientConn = await Deno.connect({ port: 4501 });
@ -688,11 +699,14 @@ Deno.test(
let responseText = ""; let responseText = "";
const buf = new Uint8Array(1024); const buf = new Uint8Array(1024);
let read; let read;
while ((read = await clientConn.read(buf)) !== null) { while ((read = await clientConn.read(buf)) !== null) {
httpConn!.close();
for (let i = 0; i < read; i++) { for (let i = 0; i < read; i++) {
responseText += String.fromCharCode(buf[i]); responseText += String.fromCharCode(buf[i]);
} }
} }
clientConn.close(); clientConn.close();
assert(/\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/.test(responseText)); assert(/\r\n[Xx]-[Hh]eader-[Tt]est: Æ\r\n/.test(responseText));
@ -704,18 +718,18 @@ Deno.test(
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerRequestWithoutPath() { async function httpServerRequestWithoutPath() {
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest(); const reqEvent = await httpConn.nextRequest();
assert(reqEvent); assert(reqEvent);
const { request, respondWith } = reqEvent; const { request, respondWith } = reqEvent;
assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/"); assertEquals(new URL(request.url).href, "http://127.0.0.1:4501/");
assertEquals(await request.text(), ""); assertEquals(await request.text(), "");
await respondWith(new Response()); await respondWith(new Response());
httpConn.close();
})(); })();
const clientConn = await Deno.connect({ port: 4501 }); const clientConn = await Deno.connect({ port: 4501 });
@ -744,6 +758,7 @@ Deno.test(
await writeRequest(clientConn); await writeRequest(clientConn);
clientConn.close(); clientConn.close();
await promise; await promise;
httpConn!.close();
}, },
); );
@ -868,11 +883,12 @@ Deno.test(function httpUpgradeWebSocketWithoutUpgradeHeader() {
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpCookieConcatenation() { async function httpCookieConcatenation() {
let httpConn: Deno.HttpConn;
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest(); const reqEvent = await httpConn.nextRequest();
assert(reqEvent); assert(reqEvent);
const { request, respondWith } = reqEvent; const { request, respondWith } = reqEvent;
@ -880,7 +896,6 @@ Deno.test(
assertEquals(await request.text(), ""); assertEquals(await request.text(), "");
assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo"); assertEquals(request.headers.get("cookie"), "foo=bar; bar=foo");
await respondWith(new Response("ok")); await respondWith(new Response("ok"));
httpConn.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4501/", { const resp = await fetch("http://127.0.0.1:4501/", {
@ -893,6 +908,7 @@ Deno.test(
const text = await resp.text(); const text = await resp.text();
assertEquals(text, "ok"); assertEquals(text, "ok");
await promise; await promise;
httpConn!.close();
}, },
); );
@ -910,7 +926,7 @@ Deno.test({ permissions: { net: true } }, async function httpServerPanic() {
httpConn.nextRequest(); httpConn.nextRequest();
await client.write(encoder.encode("\r\n\r\n")); await client.write(encoder.encode("\r\n\r\n"));
httpConn.close(); httpConn!.close();
client.close(); client.close();
listener.close(); listener.close();
@ -923,21 +939,23 @@ Deno.test(
const file = await Deno.open(tmpFile, { write: true, read: true }); const file = await Deno.open(tmpFile, { write: true, read: true });
await file.write(new Uint8Array(70 * 1024).fill(1)); // 70kb sent in 64kb + 6kb chunks await file.write(new Uint8Array(70 * 1024).fill(1)); // 70kb sent in 64kb + 6kb chunks
file.close(); file.close();
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4503 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4503 });
const conn = await listener.accept(); const conn = await listener.accept();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const ev = await httpConn.nextRequest(); const ev = await httpConn.nextRequest();
const { respondWith } = ev!; const { respondWith } = ev!;
const f = await Deno.open(tmpFile, { read: true }); const f = await Deno.open(tmpFile, { read: true });
await respondWith(new Response(f.readable, { status: 200 })); await respondWith(new Response(f.readable, { status: 200 }));
httpConn.close();
listener.close();
})(); })();
const resp = await fetch("http://127.0.0.1:4503/"); const resp = await fetch("http://127.0.0.1:4503/");
const body = await resp.arrayBuffer(); const body = await resp.arrayBuffer();
assertEquals(body.byteLength, 70 * 1024); assertEquals(body.byteLength, 70 * 1024);
await promise; await promise;
httpConn!.close();
listener.close();
}, },
); );
@ -976,7 +994,7 @@ Deno.test(
} }
assert(didThrow); assert(didThrow);
httpConn.close(); httpConn!.close();
listener.close(); listener.close();
client.close(); client.close();
}, },
@ -1018,7 +1036,7 @@ Deno.test(
await respondWith(res).catch((error: Error) => errors.push(error)); await respondWith(res).catch((error: Error) => errors.push(error));
httpConn.close(); httpConn!.close();
listener.close(); listener.close();
assert(errors.length >= 1); assert(errors.length >= 1);
@ -1047,7 +1065,7 @@ Deno.test(
)), )),
]); ]);
httpConn.close(); httpConn!.close();
listener.close(); listener.close();
clientConn.close(); clientConn.close();
}, },
@ -1133,10 +1151,11 @@ Deno.test(
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ hostname, port });
async function server() { async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const promises = new Array(10).fill(null).map(async (_, i) => { const promises = new Array(10).fill(null).map(async (_, i) => {
const event = await httpConn.nextRequest(); const event = await httpConn.nextRequest();
assert(event); assert(event);
@ -1146,8 +1165,6 @@ Deno.test(
await event.respondWith(response); await event.respondWith(response);
}); });
await Promise.all(promises); await Promise.all(promises);
httpConn.close();
listener.close();
} }
async function client() { async function client() {
@ -1159,6 +1176,8 @@ Deno.test(
} }
await Promise.all([server(), delay(100).then(client)]); await Promise.all([server(), delay(100).then(client)]);
httpConn!.close();
listener.close();
}, },
); );
@ -1213,16 +1232,15 @@ Deno.test(
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ hostname, port });
async function server() { async function server() {
const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const event = await httpConn.nextRequest() as Deno.RequestEvent; const event = await httpConn.nextRequest() as Deno.RequestEvent;
assert(event.request.body); assert(event.request.body);
const response = new Response(); const response = new Response();
await event.respondWith(response); await event.respondWith(response);
httpConn.close();
listener.close();
} }
async function client() { async function client() {
@ -1237,17 +1255,20 @@ Deno.test(
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
listener.close();
}, },
); );
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerRespondNonAsciiUint8Array() { async function httpServerRespondNonAsciiUint8Array() {
let httpConn: Deno.HttpConn;
const listener = Deno.listen({ port: 4501 });
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ port: 4501 });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1255,7 +1276,6 @@ Deno.test(
await respondWith( await respondWith(
new Response(new Uint8Array([128]), {}), new Response(new Uint8Array([128]), {}),
); );
httpConn.close();
})(); })();
const resp = await fetch("http://localhost:4501/"); const resp = await fetch("http://localhost:4501/");
@ -1264,6 +1284,7 @@ Deno.test(
assertEquals(new Uint8Array(body), new Uint8Array([128])); assertEquals(new Uint8Array(body), new Uint8Array([128]));
await promise; await promise;
httpConn!.close();
}, },
); );
@ -1276,11 +1297,12 @@ Deno.test(
async function httpServerOnUnixSocket() { async function httpServerOnUnixSocket() {
const filePath = Deno.makeTempFileSync(); const filePath = Deno.makeTempFileSync();
let httpConn: Deno.HttpConn;
const promise = (async () => { const promise = (async () => {
const listener = Deno.listen({ path: filePath, transport: "unix" }); const listener = Deno.listen({ path: filePath, transport: "unix" });
const conn = await listener.accept(); const conn = await listener.accept();
listener.close(); listener.close();
const httpConn = Deno.serveHttp(conn); httpConn = Deno.serveHttp(conn);
const reqEvent = await httpConn.nextRequest(); const reqEvent = await httpConn.nextRequest();
assert(reqEvent); assert(reqEvent);
const { request, respondWith } = reqEvent; const { request, respondWith } = reqEvent;
@ -1289,7 +1311,6 @@ Deno.test(
assertEquals(decodeURIComponent(url.host), filePath); assertEquals(decodeURIComponent(url.host), filePath);
assertEquals(url.pathname, "/path/name"); assertEquals(url.pathname, "/path/name");
await respondWith(new Response("", { headers: {} })); await respondWith(new Response("", { headers: {} }));
httpConn.close();
})(); })();
// fetch() does not supports unix domain sockets yet https://github.com/denoland/deno/issues/8821 // fetch() does not supports unix domain sockets yet https://github.com/denoland/deno/issues/8821
@ -1307,6 +1328,7 @@ Deno.test(
conn.close(); conn.close();
await promise; await promise;
httpConn!.close();
}, },
); );
@ -1324,9 +1346,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" }; const data = { hello: "deno", now: "with", compressed: "body" };
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1335,7 +1358,6 @@ Deno.test({
headers: { "content-type": "application/json" }, headers: { "content-type": "application/json" },
}); });
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1361,6 +1383,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1374,9 +1397,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" }; const data = { hello: "deno", now: "with", compressed: "body" };
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1385,7 +1409,6 @@ Deno.test({
headers: { "content-type": "application/json" }, headers: { "content-type": "application/json" },
}); });
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1413,6 +1436,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1423,10 +1447,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1438,7 +1463,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1464,6 +1488,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1474,10 +1499,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1492,7 +1518,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1518,6 +1543,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1528,10 +1554,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1543,7 +1570,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1569,6 +1595,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1579,10 +1606,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1597,7 +1625,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1627,6 +1654,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1637,10 +1665,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1655,7 +1684,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1684,6 +1712,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1694,10 +1723,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1712,7 +1742,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1738,6 +1767,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1748,10 +1778,11 @@ Deno.test({
const hostname = "localhost"; const hostname = "localhost";
const port = 4501; const port = 4501;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1766,7 +1797,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1792,6 +1822,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1807,9 +1838,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" }; const data = { hello: "deno", now: "with", compressed: "body" };
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1825,7 +1857,6 @@ Deno.test({
{ headers: { "content-type": "application/json" } }, { headers: { "content-type": "application/json" } },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1852,6 +1883,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1867,9 +1899,10 @@ Deno.test({
const data = { hello: "deno", now: "with", compressed: "body" }; const data = { hello: "deno", now: "with", compressed: "body" };
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1885,7 +1918,6 @@ Deno.test({
{ headers: { "content-type": "application/json" } }, { headers: { "content-type": "application/json" } },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1913,6 +1945,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1924,10 +1957,11 @@ Deno.test({
const port = 4501; const port = 4501;
let contentLength: string; let contentLength: string;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -1948,7 +1982,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -1977,6 +2010,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -1988,10 +2022,11 @@ Deno.test({
const port = 4501; const port = 4501;
let contentLength: string; let contentLength: string;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -2007,7 +2042,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -2037,6 +2071,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -2048,10 +2083,11 @@ Deno.test({
const port = 4501; const port = 4501;
let contentLength: string; let contentLength: string;
let httpConn: Deno.HttpConn;
async function server() { async function server() {
const listener = Deno.listen({ hostname, port }); const listener = Deno.listen({ hostname, port });
const tcpConn = await listener.accept(); const tcpConn = await listener.accept();
const httpConn = Deno.serveHttp(tcpConn); httpConn = Deno.serveHttp(tcpConn);
const e = await httpConn.nextRequest(); const e = await httpConn.nextRequest();
assert(e); assert(e);
const { request, respondWith } = e; const { request, respondWith } = e;
@ -2068,7 +2104,6 @@ Deno.test({
}, },
); );
await respondWith(response); await respondWith(response);
httpConn.close();
listener.close(); listener.close();
} }
@ -2095,6 +2130,7 @@ Deno.test({
} }
await Promise.all([server(), client()]); await Promise.all([server(), client()]);
httpConn!.close();
}, },
}); });
@ -2248,7 +2284,7 @@ Deno.test("upgradeHttp unix", {
const resp = new Response(null, { status: 101 }); const resp = new Response(null, { status: 101 });
await respondWith(resp); await respondWith(resp);
await promise; await promise;
httpConn.close(); httpConn!.close();
})(); })();
await Promise.all([server, client()]); await Promise.all([server, client()]);

View file

@ -160,10 +160,11 @@
function opAsync(opName, ...args) { function opAsync(opName, ...args) {
const promiseId = nextPromiseId++; const promiseId = nextPromiseId++;
let p = setPromise(promiseId);
const maybeError = ops[opName](promiseId, ...args); const maybeError = ops[opName](promiseId, ...args);
// Handle sync error (e.g: error parsing args) // Handle sync error (e.g: error parsing args)
if (maybeError) return unwrapOpResult(maybeError); if (maybeError) return unwrapOpResult(maybeError);
let p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult); p = PromisePrototypeThen(p, unwrapOpResult);
if (opCallTracingEnabled) { if (opCallTracingEnabled) {
// Capture a stack trace by creating a new `Error` object. We remove the // Capture a stack trace by creating a new `Error` object. We remove the
// first 6 characters (the `Error\n` prefix) to get just the stack trace. // first 6 characters (the `Error\n` prefix) to get just the stack trace.

View file

@ -28,17 +28,25 @@ use std::task::Poll;
/// turn of the event loop, which is too late for certain ops. /// turn of the event loop, which is too late for certain ops.
pub struct OpCall<T>(MaybeDone<Pin<Box<dyn Future<Output = T>>>>); pub struct OpCall<T>(MaybeDone<Pin<Box<dyn Future<Output = T>>>>);
pub enum EagerPollResult<T> {
Ready(T),
Pending(OpCall<T>),
}
impl<T> OpCall<T> { impl<T> OpCall<T> {
/// Wraps a future, and polls the inner future immediately. /// Wraps a future, and polls the inner future immediately.
/// This should be the default choice for ops. /// This should be the default choice for ops.
pub fn eager(fut: impl Future<Output = T> + 'static) -> Self { pub fn eager(fut: impl Future<Output = T> + 'static) -> EagerPollResult<T> {
let boxed = Box::pin(fut) as Pin<Box<dyn Future<Output = T>>>; let boxed = Box::pin(fut) as Pin<Box<dyn Future<Output = T>>>;
let mut inner = maybe_done(boxed); let mut inner = maybe_done(boxed);
let waker = noop_waker(); let waker = noop_waker();
let mut cx = Context::from_waker(&waker); let mut cx = Context::from_waker(&waker);
let mut pinned = Pin::new(&mut inner); let mut pinned = Pin::new(&mut inner);
let _ = pinned.as_mut().poll(&mut cx); let poll = pinned.as_mut().poll(&mut cx);
Self(inner) match poll {
Poll::Ready(_) => EagerPollResult::Ready(pinned.take_output().unwrap()),
_ => EagerPollResult::Pending(Self(inner)),
}
} }
/// Wraps a future; the inner future is polled the usual way (lazily). /// Wraps a future; the inner future is polled the usual way (lazily).

View file

@ -2145,14 +2145,54 @@ impl JsRealm {
#[inline] #[inline]
pub fn queue_async_op( pub fn queue_async_op(
scope: &v8::Isolate, state: Rc<RefCell<OpState>>,
scope: &mut v8::HandleScope,
deferred: bool,
op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)> op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)>
+ 'static, + 'static,
) { ) {
let state_rc = JsRuntime::state(scope); match OpCall::eager(op) {
let mut state = state_rc.borrow_mut(); // This calls promise.resolve() before the control goes back to userland JS. It works something
state.pending_ops.push(OpCall::eager(op)); // along the lines of:
state.have_unpolled_ops = true; //
// function opresolve(promiseId, ...) {
// getPromise(promiseId).resolve(...);
// }
// const p = setPromise();
// op.op_async(promiseId, ...); // Calls `opresolve`
// return p;
EagerPollResult::Ready((context, promise_id, op_id, mut resp))
if !deferred =>
{
let args = &[
v8::Integer::new(scope, promise_id).into(),
resp.to_v8(scope).unwrap(),
];
let realm = JsRealm::new(context);
let js_recv_cb_handle =
realm.state(scope).borrow().js_recv_cb.clone().unwrap();
state.borrow().tracker.track_async_completed(op_id);
let tc_scope = &mut v8::TryCatch::new(scope);
let js_recv_cb = js_recv_cb_handle.open(tc_scope);
let this = v8::undefined(tc_scope).into();
js_recv_cb.call(tc_scope, this, args);
}
EagerPollResult::Ready(op) => {
let ready = OpCall::ready(op);
let state_rc = JsRuntime::state(scope);
let mut state = state_rc.borrow_mut();
state.pending_ops.push(ready);
state.have_unpolled_ops = true;
}
EagerPollResult::Pending(op) => {
let state_rc = JsRuntime::state(scope);
let mut state = state_rc.borrow_mut();
state.pending_ops.push(op);
state.have_unpolled_ops = true;
}
}
} }
#[cfg(test)] #[cfg(test)]
@ -2194,7 +2234,7 @@ pub mod tests {
dispatch_count: Arc<AtomicUsize>, dispatch_count: Arc<AtomicUsize>,
} }
#[op] #[op(deferred)]
async fn op_test( async fn op_test(
rc_op_state: Rc<RefCell<OpState>>, rc_op_state: Rc<RefCell<OpState>>,
control: u8, control: u8,
@ -2255,41 +2295,6 @@ pub mod tests {
(runtime, dispatch_count) (runtime, dispatch_count)
} }
#[test]
fn test_dispatch() {
let (mut runtime, dispatch_count) = setup(Mode::Async);
runtime
.execute_script(
"filename.js",
r#"
let control = 42;
Deno.core.opAsync("op_test", control);
async function main() {
Deno.core.opAsync("op_test", control);
}
main();
"#,
)
.unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
}
#[test]
fn test_op_async_promise_id() {
let (mut runtime, _dispatch_count) = setup(Mode::Async);
runtime
.execute_script(
"filename.js",
r#"
const p = Deno.core.opAsync("op_test", 42);
if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) {
throw new Error("missing id on returned promise");
}
"#,
)
.unwrap();
}
#[test] #[test]
fn test_ref_unref_ops() { fn test_ref_unref_ops() {
let (mut runtime, _dispatch_count) = setup(Mode::Async); let (mut runtime, _dispatch_count) = setup(Mode::Async);
@ -2344,6 +2349,41 @@ pub mod tests {
} }
} }
#[test]
fn test_dispatch() {
let (mut runtime, dispatch_count) = setup(Mode::Async);
runtime
.execute_script(
"filename.js",
r#"
let control = 42;
Deno.core.opAsync("op_test", control);
async function main() {
Deno.core.opAsync("op_test", control);
}
main();
"#,
)
.unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
}
#[test]
fn test_op_async_promise_id() {
let (mut runtime, _dispatch_count) = setup(Mode::Async);
runtime
.execute_script(
"filename.js",
r#"
const p = Deno.core.opAsync("op_test", 42);
if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) {
throw new Error("missing id on returned promise");
}
"#,
)
.unwrap();
}
#[test] #[test]
fn test_dispatch_no_zero_copy_buf() { fn test_dispatch_no_zero_copy_buf() {
let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false)); let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false));

View file

@ -93,7 +93,7 @@ pub fn op_timer_handle(state: &mut OpState) -> ResourceId {
/// Waits asynchronously until either `millis` milliseconds have passed or the /// Waits asynchronously until either `millis` milliseconds have passed or the
/// [`TimerHandle`] resource given by `rid` has been canceled. /// [`TimerHandle`] resource given by `rid` has been canceled.
#[op] #[op(deferred)]
pub async fn op_sleep( pub async fn op_sleep(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
millis: u64, millis: u64,

View file

@ -420,7 +420,7 @@ pub async fn op_ws_send(
Ok(()) Ok(())
} }
#[op] #[op(deferred)]
pub async fn op_ws_close( pub async fn op_ws_close(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
rid: ResourceId, rid: ResourceId,

View file

@ -51,6 +51,7 @@ struct MacroArgs {
is_unstable: bool, is_unstable: bool,
is_v8: bool, is_v8: bool,
must_be_fast: bool, must_be_fast: bool,
deferred: bool,
} }
impl syn::parse::Parse for MacroArgs { impl syn::parse::Parse for MacroArgs {
@ -62,7 +63,7 @@ impl syn::parse::Parse for MacroArgs {
let vars: Vec<_> = vars.iter().map(Ident::to_string).collect(); let vars: Vec<_> = vars.iter().map(Ident::to_string).collect();
let vars: Vec<_> = vars.iter().map(String::as_str).collect(); let vars: Vec<_> = vars.iter().map(String::as_str).collect();
for var in vars.iter() { for var in vars.iter() {
if !["unstable", "v8", "fast"].contains(var) { if !["unstable", "v8", "fast", "deferred"].contains(var) {
return Err(syn::Error::new( return Err(syn::Error::new(
input.span(), input.span(),
"Ops expect #[op] or #[op(unstable)]", "Ops expect #[op] or #[op(unstable)]",
@ -73,6 +74,7 @@ impl syn::parse::Parse for MacroArgs {
is_unstable: vars.contains(&"unstable"), is_unstable: vars.contains(&"unstable"),
is_v8: vars.contains(&"v8"), is_v8: vars.contains(&"v8"),
must_be_fast: vars.contains(&"fast"), must_be_fast: vars.contains(&"fast"),
deferred: vars.contains(&"deferred"),
}) })
} }
} }
@ -84,6 +86,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
is_unstable, is_unstable,
is_v8, is_v8,
must_be_fast, must_be_fast,
deferred,
} = margs; } = margs;
let func = syn::parse::<syn::ItemFn>(item).expect("expected a function"); let func = syn::parse::<syn::ItemFn>(item).expect("expected a function");
let name = &func.sig.ident; let name = &func.sig.ident;
@ -110,7 +113,7 @@ pub fn op(attr: TokenStream, item: TokenStream) -> TokenStream {
let asyncness = func.sig.asyncness.is_some(); let asyncness = func.sig.asyncness.is_some();
let is_async = asyncness || is_future(&func.sig.output); let is_async = asyncness || is_future(&func.sig.output);
let v8_body = if is_async { let v8_body = if is_async {
codegen_v8_async(&core, &func, margs, asyncness) codegen_v8_async(&core, &func, margs, asyncness, deferred)
} else { } else {
codegen_v8_sync(&core, &func, margs) codegen_v8_sync(&core, &func, margs)
}; };
@ -173,6 +176,7 @@ fn codegen_v8_async(
f: &syn::ItemFn, f: &syn::ItemFn,
margs: MacroArgs, margs: MacroArgs,
asyncness: bool, asyncness: bool,
deferred: bool,
) -> TokenStream2 { ) -> TokenStream2 {
let MacroArgs { is_v8, .. } = margs; let MacroArgs { is_v8, .. } = margs;
let special_args = f let special_args = f
@ -256,7 +260,7 @@ fn codegen_v8_async(
}; };
#pre_result #pre_result
#core::_ops::queue_async_op(scope, async move { #core::_ops::queue_async_op(state, scope, #deferred, async move {
let result = #result_fut let result = #result_fut
#result_wrapper #result_wrapper
(context, promise_id, op_id, #core::_ops::to_op_result(get_class, result)) (context, promise_id, op_id, #core::_ops::to_op_result(get_class, result))

View file

@ -147,6 +147,7 @@
// cleared can actually be removed from resource table, otherwise // cleared can actually be removed from resource table, otherwise
// false positives may occur (https://github.com/denoland/deno/issues/4591) // false positives may occur (https://github.com/denoland/deno/issues/4591)
await opSanitizerDelay(); await opSanitizerDelay();
await opSanitizerDelay();
} }
if (shouldSkipSanitizers(desc)) return; if (shouldSkipSanitizers(desc)) return;

View file

@ -39,7 +39,7 @@ fn op_worker_post_message(
Ok(()) Ok(())
} }
#[op] #[op(deferred)]
async fn op_worker_recv_message( async fn op_worker_recv_message(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
) -> Result<Option<JsMessageData>, AnyError> { ) -> Result<Option<JsMessageData>, AnyError> {