mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 21:50:00 -05:00
chore(ext/web): refactor timer ops before landing op sanitizer (#22435)
Splitting the sleep and interval ops allows us to detect an interval timer. We also remove the use of the `op_async_void_deferred` call. A future PR will be able to split the op sanitizer messages for timers and intervals.
This commit is contained in:
parent
e06934fe77
commit
f705906256
4 changed files with 29 additions and 13 deletions
|
@ -271,6 +271,7 @@ pub const OP_DETAILS: phf::Map<&'static str, [&'static str; 2]> = phf_map! {
|
||||||
"op_run_status" => ["get the status of a subprocess", "awaiting the result of a `Deno.Process#status` call"],
|
"op_run_status" => ["get the status of a subprocess", "awaiting the result of a `Deno.Process#status` call"],
|
||||||
"op_seek_async" => ["seek in a file", "awaiting the result of a `Deno.File#seek` call"],
|
"op_seek_async" => ["seek in a file", "awaiting the result of a `Deno.File#seek` call"],
|
||||||
"op_signal_poll" => ["get the next signal", "un-registering a OS signal handler"],
|
"op_signal_poll" => ["get the next signal", "un-registering a OS signal handler"],
|
||||||
|
"op_sleep_interval" => ["sleep for a duration", "cancelling a `setTimeout` or `setInterval` call"],
|
||||||
"op_sleep" => ["sleep for a duration", "cancelling a `setTimeout` or `setInterval` call"],
|
"op_sleep" => ["sleep for a duration", "cancelling a `setTimeout` or `setInterval` call"],
|
||||||
"op_stat_async" => ["get file metadata", "awaiting the result of a `Deno.stat` call"],
|
"op_stat_async" => ["get file metadata", "awaiting the result of a `Deno.stat` call"],
|
||||||
"op_symlink_async" => ["create a symlink", "awaiting the result of a `Deno.symlink` call"],
|
"op_symlink_async" => ["create a symlink", "awaiting the result of a `Deno.symlink` call"],
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { core, primordials } from "ext:core/mod.js";
|
||||||
import {
|
import {
|
||||||
op_now,
|
op_now,
|
||||||
op_sleep,
|
op_sleep,
|
||||||
|
op_sleep_interval,
|
||||||
op_timer_handle,
|
op_timer_handle,
|
||||||
op_void_async_deferred,
|
|
||||||
} from "ext:core/ops";
|
} from "ext:core/ops";
|
||||||
const {
|
const {
|
||||||
ArrayPrototypePush,
|
ArrayPrototypePush,
|
||||||
|
@ -193,6 +193,7 @@ function initializeTimer(
|
||||||
task,
|
task,
|
||||||
timeout,
|
timeout,
|
||||||
timerInfo,
|
timerInfo,
|
||||||
|
repeat,
|
||||||
);
|
);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
@ -220,19 +221,13 @@ const scheduledTimers = { head: null, tail: null };
|
||||||
* after the timeout, if it hasn't been cancelled.
|
* after the timeout, if it hasn't been cancelled.
|
||||||
* @param {number} millis
|
* @param {number} millis
|
||||||
* @param {{ cancelRid: number, isRef: boolean, promise: Promise<void> }} timerInfo
|
* @param {{ cancelRid: number, isRef: boolean, promise: Promise<void> }} timerInfo
|
||||||
|
* @param {boolean} repeat
|
||||||
*/
|
*/
|
||||||
function runAfterTimeout(task, millis, timerInfo) {
|
function runAfterTimeout(task, millis, timerInfo, repeat) {
|
||||||
const cancelRid = timerInfo.cancelRid;
|
const cancelRid = timerInfo.cancelRid;
|
||||||
let sleepPromise;
|
const sleepPromise = repeat
|
||||||
// If this timeout is scheduled for 0ms it means we want it to run at the
|
? op_sleep_interval(millis, cancelRid)
|
||||||
// end of the event loop turn. There's no point in setting up a Tokio timer,
|
: op_sleep(millis, cancelRid);
|
||||||
// since its lowest resolution is 1ms. Firing of a "void async" op is better
|
|
||||||
// in this case, because the timer will take closer to 0ms instead of >1ms.
|
|
||||||
if (millis === 0) {
|
|
||||||
sleepPromise = op_void_async_deferred();
|
|
||||||
} else {
|
|
||||||
sleepPromise = op_sleep(millis, cancelRid);
|
|
||||||
}
|
|
||||||
timerInfo.promise = sleepPromise;
|
timerInfo.promise = sleepPromise;
|
||||||
if (!timerInfo.isRef) {
|
if (!timerInfo.isRef) {
|
||||||
core.unrefOpPromise(timerInfo.promise);
|
core.unrefOpPromise(timerInfo.promise);
|
||||||
|
@ -395,7 +390,8 @@ function unrefTimer(id) {
|
||||||
// for that reason: it lets promises make forward progress but can
|
// for that reason: it lets promises make forward progress but can
|
||||||
// still starve other parts of the event loop.
|
// still starve other parts of the event loop.
|
||||||
function defer(go) {
|
function defer(go) {
|
||||||
PromisePrototypeThen(op_void_async_deferred(), () => go());
|
// If we pass a delay of zero to op_sleep, it returns at the next event spin
|
||||||
|
PromisePrototypeThen(op_sleep(0, 0), () => go());
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
@ -52,6 +52,7 @@ pub use crate::message_port::MessagePort;
|
||||||
|
|
||||||
use crate::timers::op_now;
|
use crate::timers::op_now;
|
||||||
use crate::timers::op_sleep;
|
use crate::timers::op_sleep;
|
||||||
|
use crate::timers::op_sleep_interval;
|
||||||
use crate::timers::op_timer_handle;
|
use crate::timers::op_timer_handle;
|
||||||
use crate::timers::StartTime;
|
use crate::timers::StartTime;
|
||||||
pub use crate::timers::TimersPermission;
|
pub use crate::timers::TimersPermission;
|
||||||
|
@ -86,6 +87,7 @@ deno_core::extension!(deno_web,
|
||||||
op_now<P>,
|
op_now<P>,
|
||||||
op_timer_handle,
|
op_timer_handle,
|
||||||
op_sleep,
|
op_sleep,
|
||||||
|
op_sleep_interval,
|
||||||
op_transfer_arraybuffer,
|
op_transfer_arraybuffer,
|
||||||
stream_resource::op_readable_stream_resource_allocate,
|
stream_resource::op_readable_stream_resource_allocate,
|
||||||
stream_resource::op_readable_stream_resource_allocate_sized,
|
stream_resource::op_readable_stream_resource_allocate_sized,
|
||||||
|
|
|
@ -75,6 +75,16 @@ pub fn op_timer_handle(state: &mut OpState) -> ResourceId {
|
||||||
.add(TimerHandle(CancelHandle::new_rc()))
|
.add(TimerHandle(CancelHandle::new_rc()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bifurcate the op_sleep op into an interval one we can use for sanitization purposes.
|
||||||
|
#[op2(async(lazy), fast)]
|
||||||
|
pub async fn op_sleep_interval(
|
||||||
|
state: Rc<RefCell<OpState>>,
|
||||||
|
#[smi] millis: u64,
|
||||||
|
#[smi] rid: ResourceId,
|
||||||
|
) -> Result<bool, AnyError> {
|
||||||
|
op_sleep::call(state, millis, rid).await
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
|
@ -85,6 +95,13 @@ pub async fn op_sleep(
|
||||||
#[smi] millis: u64,
|
#[smi] millis: u64,
|
||||||
#[smi] rid: ResourceId,
|
#[smi] rid: ResourceId,
|
||||||
) -> Result<bool, AnyError> {
|
) -> Result<bool, AnyError> {
|
||||||
|
// If this timeout is scheduled for 0ms it means we want it to run at the
|
||||||
|
// end of the event loop turn. Since this is a lazy op, we can just return
|
||||||
|
// having already spun the event loop.
|
||||||
|
if millis == 0 {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
// If the timer is not present in the resource table it was cancelled before
|
// If the timer is not present in the resource table it was cancelled before
|
||||||
// this op was polled.
|
// this op was polled.
|
||||||
let Ok(handle) = state.borrow().resource_table.get::<TimerHandle>(rid) else {
|
let Ok(handle) = state.borrow().resource_table.get::<TimerHandle>(rid) else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue