1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-20 22:34:46 -05:00
denoland-deno/ext/node/polyfills/_next_tick.ts

165 lines
4.6 KiB
TypeScript
Raw Normal View History

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Copyright Joyent, Inc. and other Node contributors.
// TODO(petamoriken): enable prefer-primordials for node polyfills
// deno-lint-ignore-file prefer-primordials
import { core } from "ext:core/mod.js";
import { validateFunction } from "ext:deno_node/internal/validators.mjs";
import { _exiting } from "ext:deno_node/_process/exiting.ts";
import { FixedQueue } from "ext:deno_node/internal/fixed_queue.ts";
const {
getAsyncContext,
setAsyncContext,
} = core;
interface Tock {
callback: (...args: Array<unknown>) => void;
args: Array<unknown>;
snapshot: unknown;
}
let nextTickEnabled = false;
export function enableNextTick() {
nextTickEnabled = true;
}
const queue = new FixedQueue();
export function processTicksAndRejections() {
let tock: Tock;
do {
// deno-lint-ignore no-cond-assign
while (tock = queue.shift()) {
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// const asyncId = tock[async_id_symbol];
// emitBefore(asyncId, tock[trigger_async_id_symbol], tock);
const oldContext = getAsyncContext();
try {
setAsyncContext(tock.snapshot);
const callback = tock.callback;
if (tock.args === undefined) {
callback();
} else {
const args = (tock as Tock).args;
switch (args.length) {
case 1:
callback(args[0]);
break;
case 2:
callback(args[0], args[1]);
break;
case 3:
callback(args[0], args[1], args[2]);
break;
case 4:
callback(args[0], args[1], args[2], args[3]);
break;
default:
callback(...args);
}
}
} catch (e) {
reportError(e);
} finally {
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// if (destroyHooksExist())
// emitDestroy(asyncId);
setAsyncContext(oldContext);
}
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// emitAfter(asyncId);
}
core.runMicrotasks();
// FIXME(bartlomieju): Deno currently doesn't unhandled rejections
// } while (!queue.isEmpty() || processPromiseRejections());
} while (!queue.isEmpty());
core.setHasTickScheduled(false);
// FIXME(bartlomieju): Deno currently doesn't unhandled rejections
// setHasRejectionToWarn(false);
}
export function runNextTicks() {
// FIXME(bartlomieju): Deno currently doesn't unhandled rejections
// if (!hasTickScheduled() && !hasRejectionToWarn())
// runMicrotasks();
// if (!hasTickScheduled() && !hasRejectionToWarn())
// return;
perf: avoid multiple calls to runMicrotask (#26378) Improves HTTP throughput by 8-9k rps on Linux: this patch ``` Requests/sec: 145001.69 Transfer/sec: 20.74MB ``` main ``` Requests/sec: 137866.61 Transfer/sec: 19.72MB ``` The improvements comes from the reduced number of calls to `op_run_microtask` per request. Returning `true` from a macrotask callback already calls `op_run_microtask` so the extra call was redundant. Here's `--strace-ops` output for a single request: main ``` [ 4.667] op_http_wait : CompletedAsync Async [ 4.667] op_run_microtasks : Dispatched Slow [ 4.668] op_http_try_wait : Dispatched Slow [ 4.668] op_http_try_wait : Completed Slow [ 4.668] op_http_wait : Dispatched Async [ 4.668] op_http_set_response_header : Dispatched Slow [ 4.668] op_http_set_response_header : Completed Slow [ 4.669] op_http_set_response_body_text : Dispatched Slow [ 4.669] op_http_set_response_body_text : Completed Slow [ 4.669] op_run_microtasks : Completed Slow [ 4.669] op_has_tick_scheduled : Dispatched Slow [ 4.669] op_has_tick_scheduled : Completed Slow [ 4.669] op_run_microtasks : Dispatched Slow [ 4.669] op_run_microtasks : Completed Slow [ 4.669] op_run_microtasks : Dispatched Slow [ 4.669] op_run_microtasks : Completed Slow ``` this pr ``` [ 3.726] op_http_wait : CompletedAsync Async [ 3.727] op_run_microtasks : Dispatched Slow [ 3.727] op_http_try_wait : Dispatched Slow [ 3.727] op_http_try_wait : Completed Slow [ 3.727] op_http_wait : Dispatched Async [ 3.727] op_http_set_response_header : Dispatched Slow [ 3.728] op_http_set_response_header : Completed Slow [ 3.728] op_http_set_response_body_text : Dispatched Slow [ 3.728] op_http_set_response_body_text : Completed Slow [ 3.728] op_run_microtasks : Completed Slow [ 3.728] op_run_microtasks : Dispatched Slow [ 3.728] op_run_microtasks : Completed Slow ```
2024-10-18 23:12:59 -04:00
if (queue.isEmpty() || !core.hasTickScheduled()) {
return true;
}
processTicksAndRejections();
return true;
}
// `nextTick()` will not enqueue any callback when the process is about to
// exit since the callback would not have a chance to be executed.
export function nextTick(this: unknown, callback: () => void): void;
export function nextTick<T extends Array<unknown>>(
this: unknown,
callback: (...args: T) => void,
...args: T
): void;
export function nextTick<T extends Array<unknown>>(
this: unknown,
callback: (...args: T) => void,
...args: T
) {
// If we're snapshotting we don't want to push nextTick to be run. We'll
// enable next ticks in "__bootstrapNodeProcess()";
if (!nextTickEnabled) {
return;
}
validateFunction(callback, "callback");
if (_exiting) {
return;
}
// TODO(bartlomieju): seems superfluous if we don't depend on `arguments`
let args_;
switch (args.length) {
case 0:
break;
case 1:
args_ = [args[0]];
break;
case 2:
args_ = [args[0], args[1]];
break;
case 3:
args_ = [args[0], args[1], args[2]];
break;
default:
args_ = new Array(args.length);
for (let i = 0; i < args.length; i++) {
args_[i] = args[i];
}
}
if (queue.isEmpty()) {
core.setHasTickScheduled(true);
}
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// const asyncId = newAsyncId();
// const triggerAsyncId = getDefaultTriggerAsyncId();
const tickObject = {
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// [async_id_symbol]: asyncId,
// [trigger_async_id_symbol]: triggerAsyncId,
snapshot: getAsyncContext(),
callback,
args: args_,
};
// FIXME(bartlomieju): Deno currently doesn't support async hooks
// if (initHooksExist())
// emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject);
queue.push(tickObject);
}