1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00

refactor(core): JsRuntime is not a Future (#7855)

This commit rewrites deno_core::JsRuntime to not implement Future
trait.

Instead there are two separate methods:
- JsRuntime::poll_event_loop() - does single tick of event loop
- JsRuntime::run_event_loop() - runs event loop to completion
This commit is contained in:
Bartek Iwańczuk 2020-10-07 22:30:06 +02:00 committed by GitHub
parent 8bd7c936f9
commit d8879feb8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 66 deletions

View file

@ -240,7 +240,7 @@ impl Future for Worker {
// We always poll the inspector if it exists. // We always poll the inspector if it exists.
let _ = inner.inspector.as_mut().map(|i| i.poll_unpin(cx)); let _ = inner.inspector.as_mut().map(|i| i.poll_unpin(cx));
inner.waker.register(cx.waker()); inner.waker.register(cx.waker());
inner.js_runtime.poll_unpin(cx) inner.js_runtime.poll_event_loop(cx)
} }
} }

View file

@ -9,9 +9,12 @@ bindings.
This Rust crate contains the essential V8 bindings for Deno's command-line 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 interface (Deno CLI). The main abstraction here is the JsRuntime which provides
a way to execute JavaScript. The JsRuntime is modeled as a a way to execute JavaScript.
`Future<Item=(), Error=JsError>` which completes once all of its ops have
completed. 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()` 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 function to trigger the "dispatch" callback in Rust. The user is responsible for

View file

@ -260,7 +260,7 @@ fn main() {
include_str!("http_bench_bin_ops.js"), include_str!("http_bench_bin_ops.js"),
) )
.unwrap(); .unwrap();
js_runtime.await js_runtime.run_event_loop().await
}; };
runtime.block_on(future).unwrap(); runtime.block_on(future).unwrap();
} }

View file

@ -193,7 +193,7 @@ fn main() {
include_str!("http_bench_json_ops.js"), include_str!("http_bench_json_ops.js"),
) )
.unwrap(); .unwrap();
js_runtime.await js_runtime.run_event_loop().await
}; };
runtime.block_on(future).unwrap(); runtime.block_on(future).unwrap();
} }

View file

@ -453,55 +453,51 @@ impl JsRuntime {
.remove_near_heap_limit_callback(cb, heap_limit); .remove_near_heap_limit_callback(cb, heap_limit);
} }
} }
}
extern "C" fn near_heap_limit_callback<F>( /// Runs event loop to completion
data: *mut c_void, ///
current_heap_limit: usize, /// This future resolves when:
initial_heap_limit: usize, /// - there are no more pending dynamic imports
) -> usize /// - there are no more pending ops
where pub async fn run_event_loop(&mut self) -> Result<(), AnyError> {
F: FnMut(usize, usize) -> usize, poll_fn(|cx| self.poll_event_loop(cx)).await
{ }
let callback = unsafe { &mut *(data as *mut F) };
callback(current_heap_limit, initial_heap_limit)
}
impl Future for JsRuntime { /// Runs a single tick of event loop
type Output = Result<(), AnyError>; pub fn poll_event_loop(
&mut self,
cx: &mut Context,
) -> Poll<Result<(), AnyError>> {
self.shared_init();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> { let state_rc = Self::state(self.v8_isolate());
let runtime = self.get_mut();
runtime.shared_init();
let state_rc = Self::state(runtime.v8_isolate());
{ {
let state = state_rc.borrow(); let state = state_rc.borrow();
state.waker.register(cx.waker()); state.waker.register(cx.waker());
} }
// Top level modules // Top level modules
runtime.poll_mod_evaluate(cx)?; self.evaluate_pending_modules()?;
// Dynamic module loading - ie. modules loaded using "import()" // 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()); 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()); assert!(poll_imports.is_ready());
runtime.poll_dyn_imports_evaluate(cx)?; self.evaluate_dyn_imports()?;
runtime.check_promise_exceptions()?; self.check_promise_exceptions()?;
} }
// Ops // Ops
{ {
let overflow_response = runtime.poll_pending_ops(cx); let overflow_response = self.poll_pending_ops(cx);
runtime.async_op_response(overflow_response)?; self.async_op_response(overflow_response)?;
runtime.drain_macrotasks()?; self.drain_macrotasks()?;
runtime.check_promise_exceptions()?; self.check_promise_exceptions()?;
} }
let state = state_rc.borrow(); 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 { impl JsRuntimeState {
// Called by V8 during `Isolate::mod_instantiate`. // Called by V8 during `Isolate::mod_instantiate`.
pub fn module_resolve_cb( pub fn module_resolve_cb(
@ -859,7 +867,7 @@ impl JsRuntime {
debug!("received module evaluate"); debug!("received module evaluate");
return Poll::Ready(result.unwrap()); return Poll::Ready(result.unwrap());
} }
let _r = self.poll_unpin(cx)?; let _r = self.poll_event_loop(cx)?;
Poll::Pending Poll::Pending
}) })
.await .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 state_rc = Self::state(self.v8_isolate());
let context = self.global_context(); let context = self.global_context();
@ -1074,10 +1082,7 @@ impl JsRuntime {
Ok(()) Ok(())
} }
fn poll_dyn_imports_evaluate( fn evaluate_dyn_imports(&mut self) -> Result<(), AnyError> {
&mut self,
_cx: &mut Context,
) -> Result<(), AnyError> {
let state_rc = Self::state(self.v8_isolate()); let state_rc = Self::state(self.v8_isolate());
loop { loop {
@ -1440,13 +1445,13 @@ pub mod tests {
futures::executor::block_on(lazy(move |cx| f(cx))); futures::executor::block_on(lazy(move |cx| f(cx)));
} }
fn poll_until_ready<F>(future: &mut F, max_poll_count: usize) -> F::Output fn poll_until_ready(
where runtime: &mut JsRuntime,
F: Future + Unpin, max_poll_count: usize,
{ ) -> Result<(), AnyError> {
let mut cx = Context::from_waker(futures::task::noop_waker_ref()); let mut cx = Context::from_waker(futures::task::noop_waker_ref());
for _ in 0..max_poll_count { for _ in 0..max_poll_count {
match future.poll_unpin(&mut cx) { match runtime.poll_event_loop(&mut cx) {
Poll::Pending => continue, Poll::Pending => continue,
Poll::Ready(val) => return val, Poll::Ready(val) => return val,
} }
@ -1662,7 +1667,7 @@ pub mod tests {
) )
.unwrap(); .unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); 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); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1);
runtime runtime
.execute( .execute(
@ -1675,11 +1680,11 @@ pub mod tests {
) )
.unwrap(); .unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); 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(); runtime.execute("check3.js", "assert(nrecv == 2)").unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); assert_eq!(dispatch_count.load(Ordering::Relaxed), 2);
// We are idle, so the next poll should be the last. // 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); assert_eq!(dispatch_count.load(Ordering::Relaxed), 1);
// The above op never finish, but runtime can finish // The above op never finish, but runtime can finish
// because the op is an unreffed async op. // 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(); .unwrap();
assert_eq!(dispatch_count.load(Ordering::Relaxed), 1); 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 runtime
.execute("check.js", "assert(asyncRecv == 1);") .execute("check.js", "assert(asyncRecv == 1);")
.unwrap(); .unwrap();
@ -1925,7 +1930,7 @@ pub mod tests {
"#, "#,
) )
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -1938,7 +1943,7 @@ pub mod tests {
runtime runtime
.execute("core_test.js", include_str!("core_test.js")) .execute("core_test.js", include_str!("core_test.js"))
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -1964,7 +1969,7 @@ pub mod tests {
include_str!("encode_decode_test.js"), include_str!("encode_decode_test.js"),
) )
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = runtime.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -2272,7 +2277,7 @@ pub mod tests {
assert_eq!(count.load(Ordering::Relaxed), 0); assert_eq!(count.load(Ordering::Relaxed), 0);
// We should get an error here. // 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 { if let Poll::Ready(Ok(_)) = result {
unreachable!(); unreachable!();
} }
@ -2365,14 +2370,14 @@ pub mod tests {
.unwrap(); .unwrap();
// First poll runs `prepare_load` hook. // 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); assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
// Second poll actually loads modules into the isolate. // 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!(resolve_count.load(Ordering::Relaxed), 4);
assert_eq!(load_count.load(Ordering::Relaxed), 2); 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!(resolve_count.load(Ordering::Relaxed), 4);
assert_eq!(load_count.load(Ordering::Relaxed), 2); assert_eq!(load_count.load(Ordering::Relaxed), 2);
}) })
@ -2404,10 +2409,10 @@ pub mod tests {
) )
.unwrap(); .unwrap();
// First poll runs `prepare_load` hook. // 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); assert_eq!(prepare_load_count.load(Ordering::Relaxed), 1);
// Second poll triggers error // 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 at async error_async_stack.js:10:5
"#; "#;
match runtime.poll_unpin(cx) { match runtime.poll_event_loop(cx) {
Poll::Ready(Err(e)) => { Poll::Ready(Err(e)) => {
assert_eq!(e.to_string(), expected_error); assert_eq!(e.to_string(), expected_error);
} }

View file

@ -75,7 +75,6 @@ pub fn get_declaration() -> PathBuf {
mod tests { mod tests {
use deno_core::JsRuntime; use deno_core::JsRuntime;
use futures::future::lazy; use futures::future::lazy;
use futures::future::FutureExt;
use futures::task::Context; use futures::task::Context;
use futures::task::Poll; use futures::task::Poll;
@ -102,7 +101,7 @@ mod tests {
include_str!("abort_controller_test.js"), include_str!("abort_controller_test.js"),
) )
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -115,7 +114,7 @@ mod tests {
isolate isolate
.execute("event_test.js", include_str!("event_test.js")) .execute("event_test.js", include_str!("event_test.js"))
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -134,7 +133,7 @@ mod tests {
} else { } else {
unreachable!(); unreachable!();
} }
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -147,7 +146,7 @@ mod tests {
isolate isolate
.execute("event_target_test.js", include_str!("event_target_test.js")) .execute("event_target_test.js", include_str!("event_target_test.js"))
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });
@ -163,7 +162,7 @@ mod tests {
include_str!("text_encoding_test.js"), include_str!("text_encoding_test.js"),
) )
.unwrap(); .unwrap();
if let Poll::Ready(Err(_)) = isolate.poll_unpin(&mut cx) { if let Poll::Ready(Err(_)) = isolate.poll_event_loop(&mut cx) {
unreachable!(); unreachable!();
} }
}); });