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:
parent
8bd7c936f9
commit
d8879feb8c
6 changed files with 73 additions and 66 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
113
core/runtime.rs
113
core/runtime.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue