1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

fix(core): Wake up the runtime if there are ticks scheduled (#12933)

This commit is contained in:
Andreu Botella 2021-11-30 00:31:12 +01:00 committed by GitHub
parent 18a63dd977
commit 48c57001c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -825,7 +825,10 @@ impl JsRuntime {
// TODO(andreubotella) The event loop will spin as long as there are pending // TODO(andreubotella) The event loop will spin as long as there are pending
// background tasks. We should look into having V8 notify us when a // background tasks. We should look into having V8 notify us when a
// background task is done. // background task is done.
if state.have_unpolled_ops || has_pending_background_tasks { if state.have_unpolled_ops
|| has_pending_background_tasks
|| has_tick_scheduled
{
state.waker.wake(); state.waker.wake();
} }
@ -834,6 +837,7 @@ impl JsRuntime {
|| has_pending_dyn_imports || has_pending_dyn_imports
|| has_pending_dyn_module_evaluation || has_pending_dyn_module_evaluation
|| has_pending_background_tasks || has_pending_background_tasks
|| has_tick_scheduled
{ {
// pass, will be polled again // pass, will be polled again
} else { } else {
@ -846,6 +850,7 @@ impl JsRuntime {
if has_pending_refed_ops if has_pending_refed_ops
|| has_pending_dyn_imports || has_pending_dyn_imports
|| has_pending_background_tasks || has_pending_background_tasks
|| has_tick_scheduled
{ {
// pass, will be polled again // pass, will be polled again
} else if state.dyn_module_evaluate_idle_counter >= 1 { } else if state.dyn_module_evaluate_idle_counter >= 1 {
@ -2527,39 +2532,40 @@ assertEquals(1, notify_return_value);
assert_eq!(state.js_nexttick_cbs.len(), 2); assert_eq!(state.js_nexttick_cbs.len(), 2);
} }
#[tokio::test] #[test]
async fn test_has_tick_scheduled() { fn test_has_tick_scheduled() {
run_in_task(|cx| { use futures::task::ArcWake;
let macrotask = Arc::new(AtomicUsize::default());
let macrotask_ = Arc::clone(&macrotask);
let next_tick = Arc::new(AtomicUsize::default()); let macrotask = Arc::new(AtomicUsize::default());
let next_tick_ = Arc::clone(&next_tick); let macrotask_ = Arc::clone(&macrotask);
let op_macrotask = move |_: &mut OpState, _: (), _: ()| { let next_tick = Arc::new(AtomicUsize::default());
macrotask_.fetch_add(1, Ordering::Relaxed); let next_tick_ = Arc::clone(&next_tick);
Ok(())
};
let op_next_tick = move |_: &mut OpState, _: (), _: ()| { let op_macrotask = move |_: &mut OpState, _: (), _: ()| {
next_tick_.fetch_add(1, Ordering::Relaxed); macrotask_.fetch_add(1, Ordering::Relaxed);
Ok(()) Ok(())
}; };
let extension = Extension::builder() let op_next_tick = move |_: &mut OpState, _: (), _: ()| {
.ops(vec![("op_macrotask", op_sync(op_macrotask))]) next_tick_.fetch_add(1, Ordering::Relaxed);
.ops(vec![("op_next_tick", op_sync(op_next_tick))]) Ok(())
.build(); };
let mut runtime = JsRuntime::new(RuntimeOptions { let extension = Extension::builder()
extensions: vec![extension], .ops(vec![("op_macrotask", op_sync(op_macrotask))])
..Default::default() .ops(vec![("op_next_tick", op_sync(op_next_tick))])
}); .build();
runtime let mut runtime = JsRuntime::new(RuntimeOptions {
.execute_script( extensions: vec![extension],
"has_tick_scheduled.js", ..Default::default()
r#" });
runtime
.execute_script(
"has_tick_scheduled.js",
r#"
Deno.core.setMacrotaskCallback(() => { Deno.core.setMacrotaskCallback(() => {
Deno.core.opSync("op_macrotask"); Deno.core.opSync("op_macrotask");
return true; // We're done. return true; // We're done.
@ -2567,25 +2573,44 @@ assertEquals(1, notify_return_value);
Deno.core.setNextTickCallback(() => Deno.core.opSync("op_next_tick")); Deno.core.setNextTickCallback(() => Deno.core.opSync("op_next_tick"));
Deno.core.setHasTickScheduled(true); Deno.core.setHasTickScheduled(true);
"#, "#,
) )
.unwrap(); .unwrap();
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending));
assert_eq!(1, macrotask.load(Ordering::Relaxed)); struct ArcWakeImpl(Arc<AtomicUsize>);
assert_eq!(1, next_tick.load(Ordering::Relaxed)); impl ArcWake for ArcWakeImpl {
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); fn wake_by_ref(arc_self: &Arc<Self>) {
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); arc_self.0.fetch_add(1, Ordering::Relaxed);
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending)); }
let state_rc = JsRuntime::state(runtime.v8_isolate()); }
state_rc.borrow_mut().has_tick_scheduled = false;
assert!(matches!( let awoken_times = Arc::new(AtomicUsize::new(0));
runtime.poll_event_loop(cx, false), let waker =
Poll::Ready(Ok(())) futures::task::waker(Arc::new(ArcWakeImpl(awoken_times.clone())));
)); let cx = &mut Context::from_waker(&waker);
assert!(matches!(
runtime.poll_event_loop(cx, false), assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending));
Poll::Ready(Ok(())) assert_eq!(1, macrotask.load(Ordering::Relaxed));
)); assert_eq!(1, next_tick.load(Ordering::Relaxed));
}); assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1);
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending));
assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1);
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending));
assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1);
assert!(matches!(runtime.poll_event_loop(cx, false), Poll::Pending));
assert_eq!(awoken_times.swap(0, Ordering::Relaxed), 1);
let state_rc = JsRuntime::state(runtime.v8_isolate());
state_rc.borrow_mut().has_tick_scheduled = false;
assert!(matches!(
runtime.poll_event_loop(cx, false),
Poll::Ready(Ok(()))
));
assert_eq!(awoken_times.load(Ordering::Relaxed), 0);
assert!(matches!(
runtime.poll_event_loop(cx, false),
Poll::Ready(Ok(()))
));
assert_eq!(awoken_times.load(Ordering::Relaxed), 0);
} }
#[test] #[test]