use std::cell::RefCell; use std::collections::VecDeque; use std::rc::Rc; struct Task { run: v8::Global, finish: v8::Global, } type Tasks = Rc>>; pub(crate) fn clear(isolate: &mut v8::Isolate) { isolate.remove_slot::(); } pub(crate) fn init(isolate: &mut v8::Isolate) { isolate.set_slot::(Rc::new(RefCell::new(VecDeque::new()))); } pub(crate) fn enqueue<'s>( scope: &mut v8::PinScope<'s, '_>, args: v8::FunctionCallbackArguments<'s>, _result: v8::ReturnValue, ) { let (Ok(run), Ok(finish)) = ( v8::Local::::try_from(args.get(0)), v8::Local::::try_from(args.get(1)), ) else { let message = v8::String::new(scope, "Tasks require run and finish callbacks").unwrap(); let error = v8::Exception::type_error(scope, message); scope.throw_exception(error); return; }; let task = Task { run: v8::Global::new(scope, run), finish: v8::Global::new(scope, finish), }; scope .get_slot::() .unwrap() .borrow_mut() .push_back(task); if let Some(waker) = scope.get_slot::() { waker.wake_by_ref(); } } pub(crate) fn has_pending(isolate: &v8::Isolate) -> bool { !isolate.get_slot::().unwrap().borrow().is_empty() } pub(crate) fn run_next(scope: &mut v8::PinScope) -> Option { let task = scope.get_slot::().unwrap().borrow_mut().pop_front(); let Some(task) = task else { return Some(false); }; v8::tc_scope!(let scope, scope); let receiver = v8::undefined(scope).into(); let run = v8::Local::new(scope, task.run); let error = if run.call(scope, receiver, &[]).is_none() { let error = v8::Global::new(scope, scope.exception()?); scope.reset(); Some(error) } else { None }; // Post-callback steps such as repeating a timer follow its full microtask checkpoint. crate::op::checkpoint(scope); let finish = v8::Local::new(scope, task.finish); let finished = finish.call(scope, receiver, &[]).is_some(); if let Some(error) = error { scope.reset(); let error = v8::Local::new(scope, error); scope.throw_exception(error); scope.rethrow(); return None; } if !finished { scope.rethrow(); return None; } Some(true) }