mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
0de7662dab
The implementation is less than ideal - it starts a thread per timer. A better approach might be to switch to tokio and use their timer imlementation.
115 lines
3.4 KiB
Rust
115 lines
3.4 KiB
Rust
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
|
|
}
|
|
}
|