1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -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 GitHub
parent c0a684c14e
commit d2a408f452
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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);
assert!(!output.status.success());
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());
}
@ -950,7 +950,7 @@ async fn test_resolve_dns() {
let out = String::from_utf8_lossy(&output.stdout);
assert!(!output.status.success());
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());
}

View file

@ -1,2 +1,2 @@
[WILDCARD]error: Uncaught (in promise) BadResource: Bad resource ID
[WILDCARD]error: Uncaught [WILDCARD] BadResource: Bad resource ID
[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 [WILDCARD]/wasm_unreachable.js:[WILDCARD]

View file

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

View file

@ -160,10 +160,11 @@
function opAsync(opName, ...args) {
const promiseId = nextPromiseId++;
let p = setPromise(promiseId);
const maybeError = ops[opName](promiseId, ...args);
// Handle sync error (e.g: error parsing args)
if (maybeError) return unwrapOpResult(maybeError);
let p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult);
p = PromisePrototypeThen(p, unwrapOpResult);
if (opCallTracingEnabled) {
// 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.

View file

@ -28,17 +28,25 @@ use std::task::Poll;
/// turn of the event loop, which is too late for certain ops.
pub struct OpCall<T>(MaybeDone<Pin<Box<dyn Future<Output = T>>>>);
pub enum EagerPollResult<T> {
Ready(T),
Pending(OpCall<T>),
}
impl<T> OpCall<T> {
/// Wraps a future, and polls the inner future immediately.
/// 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 mut inner = maybe_done(boxed);
let waker = noop_waker();
let mut cx = Context::from_waker(&waker);
let mut pinned = Pin::new(&mut inner);
let _ = pinned.as_mut().poll(&mut cx);
Self(inner)
let poll = pinned.as_mut().poll(&mut cx);
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).

View file

@ -2145,14 +2145,54 @@ impl JsRealm {
#[inline]
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)>
+ 'static,
) {
let state_rc = JsRuntime::state(scope);
let mut state = state_rc.borrow_mut();
state.pending_ops.push(OpCall::eager(op));
state.have_unpolled_ops = true;
match OpCall::eager(op) {
// This calls promise.resolve() before the control goes back to userland JS. It works something
// along the lines of:
//
// 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)]
@ -2194,7 +2234,7 @@ pub mod tests {
dispatch_count: Arc<AtomicUsize>,
}
#[op]
#[op(deferred)]
async fn op_test(
rc_op_state: Rc<RefCell<OpState>>,
control: u8,
@ -2255,41 +2295,6 @@ pub mod tests {
(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]
fn test_ref_unref_ops() {
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]
fn test_dispatch_no_zero_copy_buf() {
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
/// [`TimerHandle`] resource given by `rid` has been canceled.
#[op]
#[op(deferred)]
pub async fn op_sleep(
state: Rc<RefCell<OpState>>,
millis: u64,

View file

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

View file

@ -51,6 +51,7 @@ struct MacroArgs {
is_unstable: bool,
is_v8: bool,
must_be_fast: bool,
deferred: bool,
}
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(String::as_str).collect();
for var in vars.iter() {
if !["unstable", "v8", "fast"].contains(var) {
if !["unstable", "v8", "fast", "deferred"].contains(var) {
return Err(syn::Error::new(
input.span(),
"Ops expect #[op] or #[op(unstable)]",
@ -73,6 +74,7 @@ impl syn::parse::Parse for MacroArgs {
is_unstable: vars.contains(&"unstable"),
is_v8: vars.contains(&"v8"),
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_v8,
must_be_fast,
deferred,
} = margs;
let func = syn::parse::<syn::ItemFn>(item).expect("expected a function");
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 is_async = asyncness || is_future(&func.sig.output);
let v8_body = if is_async {
codegen_v8_async(&core, &func, margs, asyncness)
codegen_v8_async(&core, &func, margs, asyncness, deferred)
} else {
codegen_v8_sync(&core, &func, margs)
};
@ -173,6 +176,7 @@ fn codegen_v8_async(
f: &syn::ItemFn,
margs: MacroArgs,
asyncness: bool,
deferred: bool,
) -> TokenStream2 {
let MacroArgs { is_v8, .. } = margs;
let special_args = f
@ -256,7 +260,7 @@ fn codegen_v8_async(
};
#pre_result
#core::_ops::queue_async_op(scope, async move {
#core::_ops::queue_async_op(state, scope, #deferred, async move {
let result = #result_fut
#result_wrapper
(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
// false positives may occur (https://github.com/denoland/deno/issues/4591)
await opSanitizerDelay();
await opSanitizerDelay();
}
if (shouldSkipSanitizers(desc)) return;

View file

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