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.
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)
}
}

View file

@ -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

View file

@ -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();
}

View file

@ -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();
}

View file

@ -453,55 +453,51 @@ impl JsRuntime {
.remove_near_heap_limit_callback(cb, heap_limit);
}
}
/// 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
}
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)
}
/// Runs a single tick of event loop
pub fn poll_event_loop(
&mut self,
cx: &mut Context,
) -> Poll<Result<(), AnyError>> {
self.shared_init();
impl Future for JsRuntime {
type Output = Result<(), AnyError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let runtime = self.get_mut();
runtime.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);
}

View file

@ -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!();
}
});