diff options
-rw-r--r-- | cli/worker.rs | 2 | ||||
-rw-r--r-- | core/README.md | 9 | ||||
-rw-r--r-- | core/examples/http_bench_bin_ops.rs | 2 | ||||
-rw-r--r-- | core/examples/http_bench_json_ops.rs | 2 | ||||
-rw-r--r-- | core/runtime.rs | 113 | ||||
-rw-r--r-- | op_crates/web/lib.rs | 11 |
6 files changed, 73 insertions, 66 deletions
diff --git a/cli/worker.rs b/cli/worker.rs index 488a2eeba..ea9362a6b 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -240,7 +240,7 @@ impl Future for Worker { // We always poll the inspector if it exists. let _ = inner.inspector.as_mut().map(|i| i.poll_unpin(cx)); inner.waker.register(cx.waker()); - inner.js_runtime.poll_unpin(cx) + inner.js_runtime.poll_event_loop(cx) } } diff --git a/core/README.md b/core/README.md index f6b429bb8..2438ecede 100644 --- a/core/README.md +++ b/core/README.md @@ -9,9 +9,12 @@ bindings. This Rust crate contains the essential V8 bindings for Deno's command-line interface (Deno CLI). The main abstraction here is the JsRuntime which provides -a way to execute JavaScript. The JsRuntime is modeled as a -`Future<Item=(), Error=JsError>` which completes once all of its ops have -completed. +a way to execute JavaScript. + +The JsRuntime implements an event loop abstraction for the executed code that +keeps track of all pending tasks (async ops, dynamic module loads). It is user's +responsibility to drive that loop by using `JsRuntime::run_event_loop` method - +it must be executed in the context of Rust's future executor (eg. tokio, smol). In order to bind Rust functions into JavaScript, use the `Deno.core.dispatch()` function to trigger the "dispatch" callback in Rust. The user is responsible for diff --git a/core/examples/http_bench_bin_ops.rs b/core/examples/http_bench_bin_ops.rs index 8d612f146..7335b8670 100644 --- a/core/examples/http_bench_bin_ops.rs +++ b/core/examples/http_bench_bin_ops.rs @@ -260,7 +260,7 @@ fn main() { include_str!("http_bench_bin_ops.js"), ) .unwrap(); - js_runtime.await + js_runtime.run_event_loop().await }; runtime.block_on(future).unwrap(); } diff --git a/core/examples/http_bench_json_ops.rs b/core/examples/http_bench_json_ops.rs index 106b96f36..2cf3d09e3 100644 --- a/core/examples/http_bench_json_ops.rs +++ b/core/examples/http_bench_json_ops.rs @@ -193,7 +193,7 @@ fn main() { include_str!("http_bench_json_ops.js"), ) .unwrap(); - js_runtime.await + js_runtime.run_event_loop().await }; runtime.block_on(future).unwrap(); } diff --git a/core/runtime.rs b/core/runtime.rs index ac0e812b7..f04788d4e 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -453,55 +453,51 @@ impl JsRuntime { .remove_near_heap_limit_callback(cb, heap_limit); } } -} -extern "C" fn near_heap_limit_callback<F>( - data: *mut c_void, - current_heap_limit: usize, - initial_heap_limit: usize, -) -> usize -where - F: FnMut(usize, usize) -> usize, -{ - let callback = unsafe { &mut *(data as *mut F) }; - callback(current_heap_limit, initial_heap_limit) -} - -impl Future for JsRuntime { - type Output = Result<(), AnyError>; + /// Runs event loop to completion + /// + /// This future resolves when: + /// - there are no more pending dynamic imports + /// - there are no more pending ops + pub async fn run_event_loop(&mut self) -> Result<(), AnyError> { + poll_fn(|cx| self.poll_event_loop(cx)).await + } - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { - let runtime = self.get_mut(); - runtime.shared_init(); + /// Runs a single tick of event loop + pub fn poll_event_loop( + &mut self, + cx: &mut Context, + ) -> Poll<Result<(), AnyError>> { + self.shared_init(); - let state_rc = Self::state(runtime.v8_isolate()); + let state_rc = Self::state(self.v8_isolate()); { let state = state_rc.borrow(); state.waker.register(cx.waker()); } // Top level modules - runtime.poll_mod_evaluate(cx)?; + self.evaluate_pending_modules()?; // Dynamic module loading - ie. modules loaded using "import()" { - let poll_imports = runtime.prepare_dyn_imports(cx)?; + let poll_imports = self.prepare_dyn_imports(cx)?; assert!(poll_imports.is_ready()); - let poll_imports = runtime.poll_dyn_imports(cx)?; + let poll_imports = self.poll_dyn_imports(cx)?; assert!(poll_imports.is_ready()); - runtime.poll_dyn_imports_evaluate(cx)?; + self.evaluate_dyn_imports()?; - runtime.check_promise_exceptions()?; + self.check_promise_exceptions()?; } // Ops { - let overflow_response = runtime.poll_pending_ops(cx); - runtime.async_op_response(overflow_response)?; - runtime.drain_macrotasks()?; - runtime.check_promise_exceptions()?; + let overflow_response = self.poll_pending_ops(cx); + self.async_op_response(overflow_response)?; + self.drain_macrotasks()?; + self.check_promise_exceptions()?; } let state = state_rc.borrow(); @@ -527,6 +523,18 @@ impl Future for JsRuntime { } } +extern "C" fn near_heap_limit_callback<F>( + data: *mut c_void, + current_heap_limit: usize, + initial_heap_limit: usize, +) -> usize +where + F: FnMut(usize, usize) -> usize, +{ + let callback = unsafe { &mut *(data as *mut F) }; + callback(current_heap_limit, initial_heap_limit) +} + impl JsRuntimeState { // Called by V8 during `Isolate::mod_instantiate`. pub fn module_resolve_cb( @@ -859,7 +867,7 @@ impl JsRuntime { debug!("received module evaluate"); return Poll::Ready(result.unwrap()); } - let _r = self.poll_unpin(cx)?; + let _r = self.poll_event_loop(cx)?; Poll::Pending }) .await @@ -1030,7 +1038,7 @@ impl JsRuntime { } } - fn poll_mod_evaluate(&mut self, _cx: &mut Context) -> Result<(), AnyError> { + fn evaluate_pending_modules(&mut self) -> Result<(), AnyError> { let state_rc = Self::state(self.v8_isolate()); let context = self.global_context(); @@ -1074,10 +1082,7 @@ impl JsRuntime { Ok(()) } - fn poll_dyn_imports_evaluate( - &mut self, - _cx: &mut Context, - ) -> Result<(), AnyError> { + fn evaluate_dyn_imports(&mut self) -> Result<(), AnyError> { let state_rc = Self::state(self.v8_isolate()); loop { @@ -1440,13 +1445,13 @@ pub mod tests { futures::executor::block_on(lazy(move |cx| f(cx))); } - fn poll_until_ready<F>(future: &mut F, max_poll_count: usize) -> F::Output - where - F: Future + Unpin, - { + fn poll_until_ready( + runtime: &mut JsRuntime, + max_poll_count: usize, + ) -> Result<(), AnyError> { let mut cx = Context::from_waker(futures::task::noop_waker_ref()); for _ in 0..max_poll_count { - match future.poll_unpin(&mut cx) { + match runtime.poll_event_loop(&mut cx) { Poll::Pending => continue, Poll::Ready(val) => return val, } @@ -1662,7 +1667,7 @@ pub mod tests { ) .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); runtime .execute( @@ -1675,11 +1680,11 @@ pub mod tests { ) .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); runtime.execute("check3.js", "assert(nrecv == 2)").unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); // We are idle, so the next poll should be the last. - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); }); } @@ -1703,7 +1708,7 @@ pub mod tests { assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); // The above op never finish, but runtime can finish // because the op is an unreffed async op. - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); }) } @@ -1833,7 +1838,7 @@ pub mod tests { ) .unwrap(); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); runtime .execute("check.js", "assert(asyncRecv == 1);") .unwrap(); @@ -1925,7 +1930,7 @@ pub mod tests { "#, ) .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -1938,7 +1943,7 @@ pub mod tests { runtime .execute("core_test.js", include_str!("core_test.js")) .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -1964,7 +1969,7 @@ pub mod tests { include_str!("encode_decode_test.js"), ) .unwrap(); - if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -2272,7 +2277,7 @@ pub mod tests { assert_eq!(count.load(Ordering::Relaxed), 0); // We should get an error here. - let result = runtime.poll_unpin(cx); + let result = runtime.poll_event_loop(cx); if let Poll::Ready(Ok(_)) = result { unreachable!(); } @@ -2365,14 +2370,14 @@ pub mod tests { .unwrap(); // First poll runs `prepare_load` hook. - assert!(matches!(runtime.poll_unpin(cx), Poll::Pending)); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Pending)); assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1); // Second poll actually loads modules into the isolate. - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); assert_eq!(resolve_count.load(Ordering::Relaxed), 4); assert_eq!(load_count.load(Ordering::Relaxed), 2); - assert!(matches!(runtime.poll_unpin(cx), Poll::Ready(Ok(_)))); + assert!(matches!(runtime.poll_event_loop(cx), Poll::Ready(Ok(_)))); assert_eq!(resolve_count.load(Ordering::Relaxed), 4); assert_eq!(load_count.load(Ordering::Relaxed), 2); }) @@ -2404,10 +2409,10 @@ pub mod tests { ) .unwrap(); // First poll runs `prepare_load` hook. - let _ = runtime.poll_unpin(cx); + let _ = runtime.poll_event_loop(cx); assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1); // Second poll triggers error - let _ = runtime.poll_unpin(cx); + let _ = runtime.poll_event_loop(cx); }) } @@ -2538,7 +2543,7 @@ main(); at async error_async_stack.js:10:5 "#; - match runtime.poll_unpin(cx) { + match runtime.poll_event_loop(cx) { Poll::Ready(Err(e)) => { assert_eq!(e.to_string(), expected_error); } diff --git a/op_crates/web/lib.rs b/op_crates/web/lib.rs index 26e36365b..eaf7e9f14 100644 --- a/op_crates/web/lib.rs +++ b/op_crates/web/lib.rs @@ -75,7 +75,6 @@ pub fn get_declaration() -> PathBuf { mod tests { use deno_core::JsRuntime; use futures::future::lazy; - use futures::future::FutureExt; use futures::task::Context; use futures::task::Poll; @@ -102,7 +101,7 @@ mod tests { include_str!("abort_controller_test.js"), ) .unwrap(); - if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -115,7 +114,7 @@ mod tests { isolate .execute("event_test.js", include_str!("event_test.js")) .unwrap(); - if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -134,7 +133,7 @@ mod tests { } else { unreachable!(); } - if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -147,7 +146,7 @@ mod tests { isolate .execute("event_target_test.js", include_str!("event_target_test.js")) .unwrap(); - if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) { unreachable!(); } }); @@ -163,7 +162,7 @@ mod tests { include_str!("text_encoding_test.js"), ) .unwrap(); - if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { + if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) { unreachable!(); } }); |