Codex Handbook
code-mode/src/runtime/timers.rs 114 lines
use std::thread;use std::time::Duration;use super::RuntimeCommand;use super::RuntimeState;use super::value::value_to_error_text;pub(super) struct ScheduledTimeout {    callback: v8::Global<v8::Function>,}pub(super) fn schedule_timeout(    scope: &mut v8::PinScope<'_, '_>,    args: v8::FunctionCallbackArguments,) -> Result<u64, String> {    let callback = args.get(0);    if !callback.is_function() {        return Err("setTimeout expects a function callback".to_string());    }    let callback = v8::Local::<v8::Function>::try_from(callback)        .map_err(|_| "setTimeout expects a function callback".to_string())?;    let delay_ms = args        .get(1)        .number_value(scope)        .map(normalize_delay_ms)        .unwrap_or(0);    let callback = v8::Global::new(scope, callback);    let state = scope        .get_slot_mut::<RuntimeState>()        .ok_or_else(|| "runtime state unavailable".to_string())?;    let timeout_id = state.next_timeout_id;    state.next_timeout_id = state.next_timeout_id.saturating_add(1);    let runtime_command_tx = state.runtime_command_tx.clone();    state        .pending_timeouts        .insert(timeout_id, ScheduledTimeout { callback });    thread::spawn(move || {        thread::sleep(Duration::from_millis(delay_ms));        let _ = runtime_command_tx.send(RuntimeCommand::TimeoutFired { id: timeout_id });    });    Ok(timeout_id)}pub(super) fn clear_timeout(    scope: &mut v8::PinScope<'_, '_>,    args: v8::FunctionCallbackArguments,) -> Result<(), String> {    let Some(timeout_id) = timeout_id_from_args(scope, args)? else {        return Ok(());    };    let Some(state) = scope.get_slot_mut::<RuntimeState>() else {        return Err("runtime state unavailable".to_string());    };    state.pending_timeouts.remove(&timeout_id);    Ok(())}pub(super) fn invoke_timeout_callback(    scope: &mut v8::PinScope<'_, '_>,    timeout_id: u64,) -> Result<(), String> {    let callback = {        let state = scope            .get_slot_mut::<RuntimeState>()            .ok_or_else(|| "runtime state unavailable".to_string())?;        state.pending_timeouts.remove(&timeout_id)    };    let Some(callback) = callback else {        return Ok(());    };    let tc = std::pin::pin!(v8::TryCatch::new(scope));    let mut tc = tc.init();    let callback = v8::Local::new(&tc, &callback.callback);    let receiver = v8::undefined(&tc).into();    let _ = callback.call(&tc, receiver, &[]);    if tc.has_caught() {        return Err(tc            .exception()            .map(|exception| value_to_error_text(&mut tc, exception))            .unwrap_or_else(|| "unknown code mode exception".to_string()));    }    Ok(())}fn timeout_id_from_args(    scope: &mut v8::PinScope<'_, '_>,    args: v8::FunctionCallbackArguments,) -> Result<Option<u64>, String> {    if args.length() == 0 || args.get(0).is_null_or_undefined() {        return Ok(None);    }    let Some(timeout_id) = args.get(0).number_value(scope) else {        return Err("clearTimeout expects a numeric timeout id".to_string());    };    if !timeout_id.is_finite() || timeout_id <= 0.0 {        return Ok(None);    }    Ok(Some(timeout_id.trunc().min(u64::MAX as f64) as u64))}fn normalize_delay_ms(delay_ms: f64) -> u64 {    if !delay_ms.is_finite() || delay_ms <= 0.0 {        0    } else {        delay_ms.trunc().min(u64::MAX as f64) as u64    }}