diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/01_core.js | 3 | ||||
-rw-r--r-- | core/ops.rs | 14 | ||||
-rw-r--r-- | core/runtime.rs | 122 |
3 files changed, 94 insertions, 45 deletions
diff --git a/core/01_core.js b/core/01_core.js index ab9722bc1..fda3e4977 100644 --- a/core/01_core.js +++ b/core/01_core.js @@ -160,10 +160,11 @@ function opAsync(opName, ...args) { const promiseId = nextPromiseId++; + let p = setPromise(promiseId); const maybeError = ops[opName](promiseId, ...args); // Handle sync error (e.g: error parsing args) if (maybeError) return unwrapOpResult(maybeError); - let p = PromisePrototypeThen(setPromise(promiseId), unwrapOpResult); + p = PromisePrototypeThen(p, unwrapOpResult); if (opCallTracingEnabled) { // Capture a stack trace by creating a new `Error` object. We remove the // first 6 characters (the `Error\n` prefix) to get just the stack trace. diff --git a/core/ops.rs b/core/ops.rs index d22a703bd..c14fcdd7b 100644 --- a/core/ops.rs +++ b/core/ops.rs @@ -28,17 +28,25 @@ use std::task::Poll; /// turn of the event loop, which is too late for certain ops. pub struct OpCall<T>(MaybeDone<Pin<Box<dyn Future<Output = T>>>>); +pub enum EagerPollResult<T> { + Ready(T), + Pending(OpCall<T>), +} + impl<T> OpCall<T> { /// Wraps a future, and polls the inner future immediately. /// This should be the default choice for ops. - pub fn eager(fut: impl Future<Output = T> + 'static) -> Self { + pub fn eager(fut: impl Future<Output = T> + 'static) -> EagerPollResult<T> { let boxed = Box::pin(fut) as Pin<Box<dyn Future<Output = T>>>; let mut inner = maybe_done(boxed); let waker = noop_waker(); let mut cx = Context::from_waker(&waker); let mut pinned = Pin::new(&mut inner); - let _ = pinned.as_mut().poll(&mut cx); - Self(inner) + let poll = pinned.as_mut().poll(&mut cx); + match poll { + Poll::Ready(_) => EagerPollResult::Ready(pinned.take_output().unwrap()), + _ => EagerPollResult::Pending(Self(inner)), + } } /// Wraps a future; the inner future is polled the usual way (lazily). diff --git a/core/runtime.rs b/core/runtime.rs index 4aad2bd76..2c16ddeb8 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -2145,14 +2145,54 @@ impl JsRealm { #[inline] pub fn queue_async_op( - scope: &v8::Isolate, + state: Rc<RefCell<OpState>>, + scope: &mut v8::HandleScope, + deferred: bool, op: impl Future<Output = (v8::Global<v8::Context>, PromiseId, OpId, OpResult)> + 'static, ) { - let state_rc = JsRuntime::state(scope); - let mut state = state_rc.borrow_mut(); - state.pending_ops.push(OpCall::eager(op)); - state.have_unpolled_ops = true; + match OpCall::eager(op) { + // This calls promise.resolve() before the control goes back to userland JS. It works something + // along the lines of: + // + // function opresolve(promiseId, ...) { + // getPromise(promiseId).resolve(...); + // } + // const p = setPromise(); + // op.op_async(promiseId, ...); // Calls `opresolve` + // return p; + EagerPollResult::Ready((context, promise_id, op_id, mut resp)) + if !deferred => + { + let args = &[ + v8::Integer::new(scope, promise_id).into(), + resp.to_v8(scope).unwrap(), + ]; + + let realm = JsRealm::new(context); + let js_recv_cb_handle = + realm.state(scope).borrow().js_recv_cb.clone().unwrap(); + state.borrow().tracker.track_async_completed(op_id); + + let tc_scope = &mut v8::TryCatch::new(scope); + let js_recv_cb = js_recv_cb_handle.open(tc_scope); + let this = v8::undefined(tc_scope).into(); + js_recv_cb.call(tc_scope, this, args); + } + EagerPollResult::Ready(op) => { + let ready = OpCall::ready(op); + let state_rc = JsRuntime::state(scope); + let mut state = state_rc.borrow_mut(); + state.pending_ops.push(ready); + state.have_unpolled_ops = true; + } + EagerPollResult::Pending(op) => { + let state_rc = JsRuntime::state(scope); + let mut state = state_rc.borrow_mut(); + state.pending_ops.push(op); + state.have_unpolled_ops = true; + } + } } #[cfg(test)] @@ -2194,7 +2234,7 @@ pub mod tests { dispatch_count: Arc<AtomicUsize>, } - #[op] + #[op(deferred)] async fn op_test( rc_op_state: Rc<RefCell<OpState>>, control: u8, @@ -2256,41 +2296,6 @@ pub mod tests { } #[test] - fn test_dispatch() { - let (mut runtime, dispatch_count) = setup(Mode::Async); - runtime - .execute_script( - "filename.js", - r#" - let control = 42; - Deno.core.opAsync("op_test", control); - async function main() { - Deno.core.opAsync("op_test", control); - } - main(); - "#, - ) - .unwrap(); - assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); - } - - #[test] - fn test_op_async_promise_id() { - let (mut runtime, _dispatch_count) = setup(Mode::Async); - runtime - .execute_script( - "filename.js", - r#" - const p = Deno.core.opAsync("op_test", 42); - if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) { - throw new Error("missing id on returned promise"); - } - "#, - ) - .unwrap(); - } - - #[test] fn test_ref_unref_ops() { let (mut runtime, _dispatch_count) = setup(Mode::Async); runtime @@ -2345,6 +2350,41 @@ pub mod tests { } #[test] + fn test_dispatch() { + let (mut runtime, dispatch_count) = setup(Mode::Async); + runtime + .execute_script( + "filename.js", + r#" + let control = 42; + Deno.core.opAsync("op_test", control); + async function main() { + Deno.core.opAsync("op_test", control); + } + main(); + "#, + ) + .unwrap(); + assert_eq!(dispatch_count.load(Ordering::Relaxed), 2); + } + + #[test] + fn test_op_async_promise_id() { + let (mut runtime, _dispatch_count) = setup(Mode::Async); + runtime + .execute_script( + "filename.js", + r#" + const p = Deno.core.opAsync("op_test", 42); + if (p[Symbol.for("Deno.core.internalPromiseId")] == undefined) { + throw new Error("missing id on returned promise"); + } + "#, + ) + .unwrap(); + } + + #[test] fn test_dispatch_no_zero_copy_buf() { let (mut runtime, dispatch_count) = setup(Mode::AsyncZeroCopy(false)); runtime |