mirror of
https://github.com/denoland/deno.git
synced 2025-03-04 01:44:26 -05:00
refactor(core): simplify op types and codegeneration (#18843)
About 2% improvement on WS/HTTP benchmarks, possibly unlocking more optimizations in the future. --------- Co-authored-by: Matt Mastracci <matthew@mastracci.com>
This commit is contained in:
parent
14aaa73c02
commit
77e25a656e
10 changed files with 262 additions and 268 deletions
|
@ -89,11 +89,8 @@ pub use crate::modules::ModuleType;
|
||||||
pub use crate::modules::NoopModuleLoader;
|
pub use crate::modules::NoopModuleLoader;
|
||||||
pub use crate::modules::ResolutionKind;
|
pub use crate::modules::ResolutionKind;
|
||||||
pub use crate::normalize_path::normalize_path;
|
pub use crate::normalize_path::normalize_path;
|
||||||
pub use crate::ops::Op;
|
|
||||||
pub use crate::ops::OpAsyncFuture;
|
|
||||||
pub use crate::ops::OpCall;
|
pub use crate::ops::OpCall;
|
||||||
pub use crate::ops::OpError;
|
pub use crate::ops::OpError;
|
||||||
pub use crate::ops::OpFn;
|
|
||||||
pub use crate::ops::OpId;
|
pub use crate::ops::OpId;
|
||||||
pub use crate::ops::OpResult;
|
pub use crate::ops::OpResult;
|
||||||
pub use crate::ops::OpState;
|
pub use crate::ops::OpState;
|
||||||
|
@ -135,6 +132,10 @@ pub mod _ops {
|
||||||
pub use super::ops::to_op_result;
|
pub use super::ops::to_op_result;
|
||||||
pub use super::ops::OpCtx;
|
pub use super::ops::OpCtx;
|
||||||
pub use super::ops::OpResult;
|
pub use super::ops::OpResult;
|
||||||
|
pub use super::runtime::map_async_op1;
|
||||||
|
pub use super::runtime::map_async_op2;
|
||||||
|
pub use super::runtime::map_async_op3;
|
||||||
|
pub use super::runtime::map_async_op4;
|
||||||
pub use super::runtime::queue_async_op;
|
pub use super::runtime::queue_async_op;
|
||||||
pub use super::runtime::queue_fast_async_op;
|
pub use super::runtime::queue_fast_async_op;
|
||||||
pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
|
pub use super::runtime::V8_WRAPPER_OBJECT_INDEX;
|
||||||
|
|
117
core/ops.rs
117
core/ops.rs
|
@ -8,12 +8,10 @@ use crate::runtime::JsRuntimeState;
|
||||||
use crate::OpDecl;
|
use crate::OpDecl;
|
||||||
use crate::OpsTracker;
|
use crate::OpsTracker;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use futures::future::maybe_done;
|
|
||||||
use futures::future::FusedFuture;
|
|
||||||
use futures::future::MaybeDone;
|
use futures::future::MaybeDone;
|
||||||
use futures::ready;
|
|
||||||
use futures::task::noop_waker;
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
|
use futures::FutureExt;
|
||||||
|
use pin_project::pin_project;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -22,89 +20,76 @@ use std::pin::Pin;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::rc::Weak;
|
use std::rc::Weak;
|
||||||
use std::task::Context;
|
|
||||||
use std::task::Poll;
|
|
||||||
use v8::fast_api::CFunctionInfo;
|
use v8::fast_api::CFunctionInfo;
|
||||||
use v8::fast_api::CTypeInfo;
|
use v8::fast_api::CTypeInfo;
|
||||||
|
|
||||||
/// Wrapper around a Future, which causes that Future to be polled immediately.
|
pub type RealmIdx = u16;
|
||||||
///
|
pub type PromiseId = i32;
|
||||||
/// Background: ops are stored in a `FuturesUnordered` structure which polls
|
pub type OpId = u16;
|
||||||
/// them, but without the `OpCall` wrapper this doesn't happen until the next
|
|
||||||
/// 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> {
|
#[pin_project]
|
||||||
Ready(T),
|
pub struct OpCall {
|
||||||
Pending(OpCall<T>),
|
realm_idx: RealmIdx,
|
||||||
|
promise_id: PromiseId,
|
||||||
|
op_id: OpId,
|
||||||
|
/// Future is not necessarily Unpin, so we need to pin_project.
|
||||||
|
#[pin]
|
||||||
|
fut: MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> OpCall<T> {
|
impl OpCall {
|
||||||
/// 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) -> 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 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).
|
/// Wraps a future; the inner future is polled the usual way (lazily).
|
||||||
pub fn lazy(fut: impl Future<Output = T> + 'static) -> Self {
|
pub fn pending(
|
||||||
let boxed = Box::pin(fut) as Pin<Box<dyn Future<Output = T>>>;
|
op_ctx: &OpCtx,
|
||||||
let inner = maybe_done(boxed);
|
promise_id: PromiseId,
|
||||||
Self(inner)
|
fut: Pin<Box<dyn Future<Output = OpResult> + 'static>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
realm_idx: op_ctx.realm_idx,
|
||||||
|
op_id: op_ctx.id,
|
||||||
|
promise_id,
|
||||||
|
fut: MaybeDone::Future(fut),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a future by specifying its output. This is basically the same as
|
/// Create a future by specifying its output. This is basically the same as
|
||||||
/// `async { value }` or `futures::future::ready(value)`.
|
/// `async { value }` or `futures::future::ready(value)`.
|
||||||
pub fn ready(value: T) -> Self {
|
pub fn ready(op_ctx: &OpCtx, promise_id: PromiseId, value: OpResult) -> Self {
|
||||||
Self(MaybeDone::Done(value))
|
Self {
|
||||||
|
realm_idx: op_ctx.realm_idx,
|
||||||
|
op_id: op_ctx.id,
|
||||||
|
promise_id,
|
||||||
|
fut: MaybeDone::Done(value),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Future for OpCall<T> {
|
impl Future for OpCall {
|
||||||
type Output = T;
|
type Output = (RealmIdx, PromiseId, OpId, OpResult);
|
||||||
|
|
||||||
fn poll(
|
fn poll(
|
||||||
self: std::pin::Pin<&mut Self>,
|
self: std::pin::Pin<&mut Self>,
|
||||||
cx: &mut std::task::Context<'_>,
|
cx: &mut std::task::Context<'_>,
|
||||||
) -> std::task::Poll<Self::Output> {
|
) -> std::task::Poll<Self::Output> {
|
||||||
// TODO(piscisaureus): safety comment
|
let realm_idx = self.realm_idx;
|
||||||
#[allow(clippy::undocumented_unsafe_blocks)]
|
let promise_id = self.promise_id;
|
||||||
let inner = unsafe { &mut self.get_unchecked_mut().0 };
|
let op_id = self.op_id;
|
||||||
let mut pinned = Pin::new(inner);
|
let fut = &mut *self.project().fut;
|
||||||
ready!(pinned.as_mut().poll(cx));
|
match fut {
|
||||||
Poll::Ready(pinned.as_mut().take_output().unwrap())
|
MaybeDone::Done(_) => {
|
||||||
|
// Let's avoid using take_output as it keeps our Pin::box
|
||||||
|
let res = std::mem::replace(fut, MaybeDone::Gone);
|
||||||
|
let MaybeDone::Done(res) = res
|
||||||
|
else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
std::task::Poll::Ready(res)
|
||||||
}
|
}
|
||||||
}
|
MaybeDone::Future(f) => f.poll_unpin(cx),
|
||||||
|
MaybeDone::Gone => std::task::Poll::Pending,
|
||||||
impl<F> FusedFuture for OpCall<F>
|
}
|
||||||
where
|
.map(move |res| (realm_idx, promise_id, op_id, res))
|
||||||
F: Future,
|
|
||||||
{
|
|
||||||
fn is_terminated(&self) -> bool {
|
|
||||||
self.0.is_terminated()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub type RealmIdx = usize;
|
|
||||||
pub type PromiseId = i32;
|
|
||||||
pub type OpAsyncFuture = OpCall<(PromiseId, OpId, OpResult)>;
|
|
||||||
pub type OpFn =
|
|
||||||
fn(&mut v8::HandleScope, v8::FunctionCallbackArguments, v8::ReturnValue);
|
|
||||||
pub type OpId = usize;
|
|
||||||
|
|
||||||
pub enum Op {
|
|
||||||
Sync(OpResult),
|
|
||||||
Async(OpAsyncFuture),
|
|
||||||
NotFound,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OpResult {
|
pub enum OpResult {
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl OpsTracker {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn metrics_mut(&self, id: OpId) -> RefMut<OpMetrics> {
|
fn metrics_mut(&self, id: OpId) -> RefMut<OpMetrics> {
|
||||||
RefMut::map(self.ops.borrow_mut(), |ops| &mut ops[id])
|
RefMut::map(self.ops.borrow_mut(), |ops| &mut ops[id as usize])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
152
core/runtime.rs
152
core/runtime.rs
|
@ -35,8 +35,10 @@ use futures::channel::oneshot;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
use futures::future::Future;
|
use futures::future::Future;
|
||||||
use futures::future::FutureExt;
|
use futures::future::FutureExt;
|
||||||
|
use futures::future::MaybeDone;
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::stream::FuturesUnordered;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
use futures::task::noop_waker;
|
||||||
use futures::task::AtomicWaker;
|
use futures::task::AtomicWaker;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
@ -45,6 +47,7 @@ use std::collections::HashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -53,8 +56,6 @@ use std::task::Context;
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use v8::OwnedIsolate;
|
use v8::OwnedIsolate;
|
||||||
|
|
||||||
type PendingOpFuture = OpCall<(RealmIdx, PromiseId, OpId, OpResult)>;
|
|
||||||
|
|
||||||
pub enum Snapshot {
|
pub enum Snapshot {
|
||||||
Static(&'static [u8]),
|
Static(&'static [u8]),
|
||||||
JustCreated(v8::StartupData),
|
JustCreated(v8::StartupData),
|
||||||
|
@ -165,7 +166,7 @@ pub struct JsRuntimeState {
|
||||||
dyn_module_evaluate_idle_counter: u32,
|
dyn_module_evaluate_idle_counter: u32,
|
||||||
pub(crate) source_map_getter: Option<Rc<Box<dyn SourceMapGetter>>>,
|
pub(crate) source_map_getter: Option<Rc<Box<dyn SourceMapGetter>>>,
|
||||||
pub(crate) source_map_cache: Rc<RefCell<SourceMapCache>>,
|
pub(crate) source_map_cache: Rc<RefCell<SourceMapCache>>,
|
||||||
pub(crate) pending_ops: FuturesUnordered<PendingOpFuture>,
|
pub(crate) pending_ops: FuturesUnordered<OpCall>,
|
||||||
pub(crate) have_unpolled_ops: bool,
|
pub(crate) have_unpolled_ops: bool,
|
||||||
pub(crate) op_state: Rc<RefCell<OpState>>,
|
pub(crate) op_state: Rc<RefCell<OpState>>,
|
||||||
pub(crate) shared_array_buffer_store: Option<SharedArrayBufferStore>,
|
pub(crate) shared_array_buffer_store: Option<SharedArrayBufferStore>,
|
||||||
|
@ -360,7 +361,7 @@ impl JsRuntime {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(id, decl)| {
|
.map(|(id, decl)| {
|
||||||
OpCtx::new(id, 0, Rc::new(decl), op_state.clone(), weak.clone())
|
OpCtx::new(id as u16, 0, Rc::new(decl), op_state.clone(), weak.clone())
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_boxed_slice();
|
.into_boxed_slice();
|
||||||
|
@ -610,7 +611,7 @@ impl JsRuntime {
|
||||||
/// constructed.
|
/// constructed.
|
||||||
pub fn create_realm(&mut self) -> Result<JsRealm, Error> {
|
pub fn create_realm(&mut self) -> Result<JsRealm, Error> {
|
||||||
let realm = {
|
let realm = {
|
||||||
let realm_idx = self.state.borrow().known_realms.len();
|
let realm_idx = self.state.borrow().known_realms.len() as u16;
|
||||||
|
|
||||||
let op_ctxs: Box<[OpCtx]> = self
|
let op_ctxs: Box<[OpCtx]> = self
|
||||||
.global_realm()
|
.global_realm()
|
||||||
|
@ -2231,7 +2232,7 @@ impl JsRuntime {
|
||||||
{
|
{
|
||||||
let (realm_idx, promise_id, op_id, resp) = item;
|
let (realm_idx, promise_id, op_id, resp) = item;
|
||||||
state.op_state.borrow().tracker.track_async_completed(op_id);
|
state.op_state.borrow().tracker.track_async_completed(op_id);
|
||||||
responses_per_realm[realm_idx].push((promise_id, resp));
|
responses_per_realm[realm_idx as usize].push((promise_id, resp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2335,7 +2336,7 @@ impl JsRuntime {
|
||||||
{
|
{
|
||||||
let (realm_idx, promise_id, op_id, mut resp) = item;
|
let (realm_idx, promise_id, op_id, mut resp) = item;
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
state.known_realms[realm_idx],
|
state.known_realms[realm_idx as usize],
|
||||||
state.global_realm.as_ref().unwrap().context()
|
state.global_realm.as_ref().unwrap().context()
|
||||||
);
|
);
|
||||||
realm_state.unrefed_ops.remove(&promise_id);
|
realm_state.unrefed_ops.remove(&promise_id);
|
||||||
|
@ -2382,27 +2383,106 @@ impl JsRuntime {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn queue_fast_async_op(
|
pub fn queue_fast_async_op<R: serde::Serialize + 'static>(
|
||||||
ctx: &OpCtx,
|
ctx: &OpCtx,
|
||||||
op: impl Future<Output = (RealmIdx, PromiseId, OpId, OpResult)> + 'static,
|
promise_id: PromiseId,
|
||||||
|
op: impl Future<Output = Result<R, Error>> + 'static,
|
||||||
) {
|
) {
|
||||||
let runtime_state = match ctx.runtime_state.upgrade() {
|
let runtime_state = match ctx.runtime_state.upgrade() {
|
||||||
Some(rc_state) => rc_state,
|
Some(rc_state) => rc_state,
|
||||||
// atleast 1 Rc is held by the JsRuntime.
|
// atleast 1 Rc is held by the JsRuntime.
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
};
|
};
|
||||||
|
let get_class = {
|
||||||
|
let state = RefCell::borrow(&ctx.state);
|
||||||
|
state.tracker.track_async(ctx.id);
|
||||||
|
state.get_error_class_fn
|
||||||
|
};
|
||||||
|
let fut = op
|
||||||
|
.map(|result| crate::_ops::to_op_result(get_class, result))
|
||||||
|
.boxed_local();
|
||||||
let mut state = runtime_state.borrow_mut();
|
let mut state = runtime_state.borrow_mut();
|
||||||
state.pending_ops.push(OpCall::lazy(op));
|
state
|
||||||
|
.pending_ops
|
||||||
|
.push(OpCall::pending(ctx, promise_id, fut));
|
||||||
state.have_unpolled_ops = true;
|
state.have_unpolled_ops = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn map_async_op1<R: serde::Serialize + 'static>(
|
||||||
|
ctx: &OpCtx,
|
||||||
|
op: impl Future<Output = Result<R, Error>> + 'static,
|
||||||
|
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
|
||||||
|
let get_class = {
|
||||||
|
let state = RefCell::borrow(&ctx.state);
|
||||||
|
state.tracker.track_async(ctx.id);
|
||||||
|
state.get_error_class_fn
|
||||||
|
};
|
||||||
|
|
||||||
|
let fut = op
|
||||||
|
.map(|result| crate::_ops::to_op_result(get_class, result))
|
||||||
|
.boxed_local();
|
||||||
|
MaybeDone::Future(fut)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn map_async_op2<R: serde::Serialize + 'static>(
|
||||||
|
ctx: &OpCtx,
|
||||||
|
op: impl Future<Output = R> + 'static,
|
||||||
|
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
|
||||||
|
let state = RefCell::borrow(&ctx.state);
|
||||||
|
state.tracker.track_async(ctx.id);
|
||||||
|
|
||||||
|
let fut = op.map(|result| OpResult::Ok(result.into())).boxed_local();
|
||||||
|
MaybeDone::Future(fut)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn map_async_op3<R: serde::Serialize + 'static>(
|
||||||
|
ctx: &OpCtx,
|
||||||
|
op: Result<impl Future<Output = Result<R, Error>> + 'static, Error>,
|
||||||
|
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
|
||||||
|
let get_class = {
|
||||||
|
let state = RefCell::borrow(&ctx.state);
|
||||||
|
state.tracker.track_async(ctx.id);
|
||||||
|
state.get_error_class_fn
|
||||||
|
};
|
||||||
|
|
||||||
|
match op {
|
||||||
|
Err(err) => MaybeDone::Done(OpResult::Err(OpError::new(get_class, err))),
|
||||||
|
Ok(fut) => MaybeDone::Future(
|
||||||
|
fut
|
||||||
|
.map(|result| crate::_ops::to_op_result(get_class, result))
|
||||||
|
.boxed_local(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn map_async_op4<R: serde::Serialize + 'static>(
|
||||||
|
ctx: &OpCtx,
|
||||||
|
op: Result<impl Future<Output = R> + 'static, Error>,
|
||||||
|
) -> MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>> {
|
||||||
|
let get_class = {
|
||||||
|
let state = RefCell::borrow(&ctx.state);
|
||||||
|
state.tracker.track_async(ctx.id);
|
||||||
|
state.get_error_class_fn
|
||||||
|
};
|
||||||
|
|
||||||
|
match op {
|
||||||
|
Err(err) => MaybeDone::Done(OpResult::Err(OpError::new(get_class, err))),
|
||||||
|
Ok(fut) => MaybeDone::Future(
|
||||||
|
fut.map(|result| OpResult::Ok(result.into())).boxed_local(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn queue_async_op<'s>(
|
pub fn queue_async_op<'s>(
|
||||||
ctx: &OpCtx,
|
ctx: &OpCtx,
|
||||||
scope: &'s mut v8::HandleScope,
|
scope: &'s mut v8::HandleScope,
|
||||||
deferred: bool,
|
deferred: bool,
|
||||||
op: impl Future<Output = (RealmIdx, PromiseId, OpId, OpResult)> + 'static,
|
promise_id: PromiseId,
|
||||||
|
mut op: MaybeDone<Pin<Box<dyn Future<Output = OpResult>>>>,
|
||||||
) -> Option<v8::Local<'s, v8::Value>> {
|
) -> Option<v8::Local<'s, v8::Value>> {
|
||||||
let runtime_state = match ctx.runtime_state.upgrade() {
|
let runtime_state = match ctx.runtime_state.upgrade() {
|
||||||
Some(rc_state) => rc_state,
|
Some(rc_state) => rc_state,
|
||||||
|
@ -2415,32 +2495,40 @@ pub fn queue_async_op<'s>(
|
||||||
// deno_core doesn't currently support such exposure, even though embedders
|
// deno_core doesn't currently support such exposure, even though embedders
|
||||||
// can cause them, so we panic in debug mode (since the check is expensive).
|
// can cause them, so we panic in debug mode (since the check is expensive).
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
runtime_state.borrow().known_realms[ctx.realm_idx].to_local(scope),
|
runtime_state.borrow().known_realms[ctx.realm_idx as usize].to_local(scope),
|
||||||
Some(scope.get_current_context())
|
Some(scope.get_current_context())
|
||||||
);
|
);
|
||||||
|
|
||||||
match OpCall::eager(op) {
|
// All ops are polled immediately
|
||||||
// If the result is ready we'll just return it straight to the caller, so
|
let waker = noop_waker();
|
||||||
// we don't have to invoke a JS callback to respond. // This works under the
|
let mut cx = Context::from_waker(&waker);
|
||||||
// assumption that `()` return value is serialized as `null`.
|
|
||||||
EagerPollResult::Ready((_, _, op_id, mut resp)) if !deferred => {
|
// Note that MaybeDone returns () from the future
|
||||||
let resp = resp.to_v8(scope).unwrap();
|
let op_call = match op.poll_unpin(&mut cx) {
|
||||||
ctx.state.borrow_mut().tracker.track_async_completed(op_id);
|
Poll::Pending => {
|
||||||
return Some(resp);
|
let MaybeDone::Future(fut) = op else {
|
||||||
}
|
unreachable!()
|
||||||
EagerPollResult::Ready(op) => {
|
};
|
||||||
let ready = OpCall::ready(op);
|
OpCall::pending(ctx, promise_id, fut)
|
||||||
let mut state = runtime_state.borrow_mut();
|
|
||||||
state.pending_ops.push(ready);
|
|
||||||
state.have_unpolled_ops = true;
|
|
||||||
}
|
|
||||||
EagerPollResult::Pending(op) => {
|
|
||||||
let mut state = runtime_state.borrow_mut();
|
|
||||||
state.pending_ops.push(op);
|
|
||||||
state.have_unpolled_ops = true;
|
|
||||||
}
|
}
|
||||||
|
Poll::Ready(_) => {
|
||||||
|
let mut op_result = Pin::new(&mut op).take_output().unwrap();
|
||||||
|
// If the op is ready and is not marked as deferred we can immediately return
|
||||||
|
// the result.
|
||||||
|
if !deferred {
|
||||||
|
ctx.state.borrow_mut().tracker.track_async_completed(ctx.id);
|
||||||
|
return Some(op_result.to_v8(scope).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpCall::ready(ctx, promise_id, op_result)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Otherwise we will push it to the `pending_ops` and let it be polled again
|
||||||
|
// or resolved on the next tick of the event loop.
|
||||||
|
let mut state = runtime_state.borrow_mut();
|
||||||
|
state.pending_ops.push(op_call);
|
||||||
|
state.have_unpolled_ops = true;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,40 +245,15 @@ pub(crate) fn generate(
|
||||||
}
|
}
|
||||||
|
|
||||||
if optimizer.is_async {
|
if optimizer.is_async {
|
||||||
// Referenced variables are declared in parent block.
|
|
||||||
let track_async = q!({
|
|
||||||
let __op_id = __ctx.id;
|
|
||||||
let __state = ::std::cell::RefCell::borrow(&__ctx.state);
|
|
||||||
__state.tracker.track_async(__op_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
output_transforms.push_tokens(&track_async);
|
|
||||||
|
|
||||||
let queue_future = if optimizer.returns_result {
|
let queue_future = if optimizer.returns_result {
|
||||||
q!({
|
q!({
|
||||||
let realm_idx = __ctx.realm_idx;
|
let result = _ops::queue_fast_async_op(__ctx, __promise_id, result);
|
||||||
let __get_class = __state.get_error_class_fn;
|
|
||||||
let result = _ops::queue_fast_async_op(__ctx, async move {
|
|
||||||
let result = result.await;
|
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
__promise_id,
|
|
||||||
__op_id,
|
|
||||||
_ops::to_op_result(__get_class, result),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
q!({
|
q!({
|
||||||
let realm_idx = __ctx.realm_idx;
|
let result =
|
||||||
let result = _ops::queue_fast_async_op(__ctx, async move {
|
_ops::queue_fast_async_op(__ctx, __promise_id, async move {
|
||||||
let result = result.await;
|
Ok(result.await)
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
__promise_id,
|
|
||||||
__op_id,
|
|
||||||
_ops::OpResult::Ok(result.into()),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
86
ops/lib.rs
86
ops/lib.rs
|
@ -258,32 +258,55 @@ fn codegen_v8_async(
|
||||||
let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 1, asyncness);
|
let (arg_decls, args_tail, _) = codegen_args(core, f, rust_i0, 1, asyncness);
|
||||||
let type_params = exclude_lifetime_params(&f.sig.generics.params);
|
let type_params = exclude_lifetime_params(&f.sig.generics.params);
|
||||||
|
|
||||||
let (pre_result, mut result_fut) = match asyncness {
|
let wrapper = match (asyncness, is_result(&f.sig.output)) {
|
||||||
true => (
|
(true, true) => {
|
||||||
quote! {},
|
|
||||||
quote! { Self::call::<#type_params>(#args_head #args_tail).await; },
|
|
||||||
),
|
|
||||||
false => (
|
|
||||||
quote! { let result_fut = Self::call::<#type_params>(#args_head #args_tail); },
|
|
||||||
quote! { result_fut.await; },
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let result_wrapper = match is_result(&f.sig.output) {
|
|
||||||
true => {
|
|
||||||
// Support `Result<impl Future<Output = Result<T, AnyError>> + 'static, AnyError>`
|
|
||||||
if !asyncness {
|
|
||||||
result_fut = quote! { result_fut; };
|
|
||||||
quote! {
|
quote! {
|
||||||
let result = match result {
|
let fut = #core::_ops::map_async_op1(ctx, Self::call::<#type_params>(#args_head #args_tail));
|
||||||
Ok(fut) => fut.await,
|
let maybe_response = #core::_ops::queue_async_op(
|
||||||
Err(e) => return (realm_idx, promise_id, op_id, #core::_ops::to_op_result::<()>(get_class, Err(e))),
|
ctx,
|
||||||
};
|
scope,
|
||||||
}
|
#deferred,
|
||||||
} else {
|
promise_id,
|
||||||
quote! {}
|
fut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(true, false) => {
|
||||||
|
quote! {
|
||||||
|
let fut = #core::_ops::map_async_op2(ctx, Self::call::<#type_params>(#args_head #args_tail));
|
||||||
|
let maybe_response = #core::_ops::queue_async_op(
|
||||||
|
ctx,
|
||||||
|
scope,
|
||||||
|
#deferred,
|
||||||
|
promise_id,
|
||||||
|
fut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
quote! {
|
||||||
|
let fut = #core::_ops::map_async_op3(ctx, Self::call::<#type_params>(#args_head #args_tail));
|
||||||
|
let maybe_response = #core::_ops::queue_async_op(
|
||||||
|
ctx,
|
||||||
|
scope,
|
||||||
|
#deferred,
|
||||||
|
promise_id,
|
||||||
|
fut,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(false, false) => {
|
||||||
|
quote! {
|
||||||
|
let fut = #core::_ops::map_async_op4(ctx, Self::call::<#type_params>(#args_head #args_tail));
|
||||||
|
let maybe_response = #core::_ops::queue_async_op(
|
||||||
|
ctx,
|
||||||
|
scope,
|
||||||
|
#deferred,
|
||||||
|
promise_id,
|
||||||
|
fut,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false => quote! { let result = Ok(result); },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -293,8 +316,6 @@ fn codegen_v8_async(
|
||||||
&*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value()
|
&*(#core::v8::Local::<#core::v8::External>::cast(args.data()).value()
|
||||||
as *const #core::_ops::OpCtx)
|
as *const #core::_ops::OpCtx)
|
||||||
};
|
};
|
||||||
let op_id = ctx.id;
|
|
||||||
let realm_idx = ctx.realm_idx;
|
|
||||||
|
|
||||||
let promise_id = args.get(0);
|
let promise_id = args.get(0);
|
||||||
let promise_id = #core::v8::Local::<#core::v8::Integer>::try_from(promise_id)
|
let promise_id = #core::v8::Local::<#core::v8::Integer>::try_from(promise_id)
|
||||||
|
@ -310,20 +331,7 @@ fn codegen_v8_async(
|
||||||
};
|
};
|
||||||
|
|
||||||
#arg_decls
|
#arg_decls
|
||||||
|
#wrapper
|
||||||
// Track async call & get copy of get_error_class_fn
|
|
||||||
let get_class = {
|
|
||||||
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
|
||||||
state.tracker.track_async(op_id);
|
|
||||||
state.get_error_class_fn
|
|
||||||
};
|
|
||||||
|
|
||||||
#pre_result
|
|
||||||
let maybe_response = #core::_ops::queue_async_op(ctx, scope, #deferred, async move {
|
|
||||||
let result = #result_fut
|
|
||||||
#result_wrapper
|
|
||||||
(realm_idx, promise_id, op_id, #core::_ops::to_op_result(get_class, result))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(response) = maybe_response {
|
if let Some(response) = maybe_response {
|
||||||
rv.set(response);
|
rv.set(response);
|
||||||
|
|
|
@ -56,8 +56,6 @@ impl op_void_async {
|
||||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||||
as *const deno_core::_ops::OpCtx)
|
as *const deno_core::_ops::OpCtx)
|
||||||
};
|
};
|
||||||
let op_id = ctx.id;
|
|
||||||
let realm_idx = ctx.realm_idx;
|
|
||||||
let promise_id = args.get(0);
|
let promise_id = args.get(0);
|
||||||
let promise_id = deno_core::v8::Local::<
|
let promise_id = deno_core::v8::Local::<
|
||||||
deno_core::v8::Integer,
|
deno_core::v8::Integer,
|
||||||
|
@ -74,25 +72,13 @@ impl op_void_async {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let get_class = {
|
let fut = deno_core::_ops::map_async_op2(ctx, Self::call());
|
||||||
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
|
||||||
state.tracker.track_async(op_id);
|
|
||||||
state.get_error_class_fn
|
|
||||||
};
|
|
||||||
let maybe_response = deno_core::_ops::queue_async_op(
|
let maybe_response = deno_core::_ops::queue_async_op(
|
||||||
ctx,
|
ctx,
|
||||||
scope,
|
scope,
|
||||||
false,
|
false,
|
||||||
async move {
|
|
||||||
let result = Self::call().await;
|
|
||||||
let result = Ok(result);
|
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
promise_id,
|
promise_id,
|
||||||
op_id,
|
fut,
|
||||||
deno_core::_ops::to_op_result(get_class, result),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Some(response) = maybe_response {
|
if let Some(response) = maybe_response {
|
||||||
rv.set(response);
|
rv.set(response);
|
||||||
|
@ -116,16 +102,10 @@ fn op_void_async_fast_fn<'scope>(
|
||||||
};
|
};
|
||||||
let op_state = __ctx.state.clone();
|
let op_state = __ctx.state.clone();
|
||||||
let result = op_void_async::call();
|
let result = op_void_async::call();
|
||||||
let __op_id = __ctx.id;
|
|
||||||
let __state = ::std::cell::RefCell::borrow(&__ctx.state);
|
|
||||||
__state.tracker.track_async(__op_id);
|
|
||||||
let realm_idx = __ctx.realm_idx;
|
|
||||||
let result = _ops::queue_fast_async_op(
|
let result = _ops::queue_fast_async_op(
|
||||||
__ctx,
|
__ctx,
|
||||||
async move {
|
__promise_id,
|
||||||
let result = result.await;
|
async move { Ok(result.await) },
|
||||||
(realm_idx, __promise_id, __op_id, _ops::OpResult::Ok(result.into()))
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,8 +56,6 @@ impl op_async_result {
|
||||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||||
as *const deno_core::_ops::OpCtx)
|
as *const deno_core::_ops::OpCtx)
|
||||||
};
|
};
|
||||||
let op_id = ctx.id;
|
|
||||||
let realm_idx = ctx.realm_idx;
|
|
||||||
let promise_id = args.get(0);
|
let promise_id = args.get(0);
|
||||||
let promise_id = deno_core::v8::Local::<
|
let promise_id = deno_core::v8::Local::<
|
||||||
deno_core::v8::Integer,
|
deno_core::v8::Integer,
|
||||||
|
@ -85,24 +83,16 @@ impl op_async_result {
|
||||||
return deno_core::_ops::throw_type_error(scope, msg);
|
return deno_core::_ops::throw_type_error(scope, msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let get_class = {
|
let fut = deno_core::_ops::map_async_op1(
|
||||||
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
ctx,
|
||||||
state.tracker.track_async(op_id);
|
Self::call(ctx.state.clone(), arg_0),
|
||||||
state.get_error_class_fn
|
);
|
||||||
};
|
|
||||||
let maybe_response = deno_core::_ops::queue_async_op(
|
let maybe_response = deno_core::_ops::queue_async_op(
|
||||||
ctx,
|
ctx,
|
||||||
scope,
|
scope,
|
||||||
false,
|
false,
|
||||||
async move {
|
|
||||||
let result = Self::call(ctx.state.clone(), arg_0).await;
|
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
promise_id,
|
promise_id,
|
||||||
op_id,
|
fut,
|
||||||
deno_core::_ops::to_op_result(get_class, result),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Some(response) = maybe_response {
|
if let Some(response) = maybe_response {
|
||||||
rv.set(response);
|
rv.set(response);
|
||||||
|
@ -127,16 +117,5 @@ fn op_async_result_fast_fn<'scope>(
|
||||||
};
|
};
|
||||||
let state = __ctx.state.clone();
|
let state = __ctx.state.clone();
|
||||||
let result = op_async_result::call(state, rid);
|
let result = op_async_result::call(state, rid);
|
||||||
let __op_id = __ctx.id;
|
let result = _ops::queue_fast_async_op(__ctx, __promise_id, result);
|
||||||
let __state = ::std::cell::RefCell::borrow(&__ctx.state);
|
|
||||||
__state.tracker.track_async(__op_id);
|
|
||||||
let realm_idx = __ctx.realm_idx;
|
|
||||||
let __get_class = __state.get_error_class_fn;
|
|
||||||
let result = _ops::queue_fast_async_op(
|
|
||||||
__ctx,
|
|
||||||
async move {
|
|
||||||
let result = result.await;
|
|
||||||
(realm_idx, __promise_id, __op_id, _ops::to_op_result(__get_class, result))
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,6 @@ impl send_stdin {
|
||||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||||
as *const deno_core::_ops::OpCtx)
|
as *const deno_core::_ops::OpCtx)
|
||||||
};
|
};
|
||||||
let op_id = ctx.id;
|
|
||||||
let realm_idx = ctx.realm_idx;
|
|
||||||
let promise_id = args.get(0);
|
let promise_id = args.get(0);
|
||||||
let promise_id = deno_core::v8::Local::<
|
let promise_id = deno_core::v8::Local::<
|
||||||
deno_core::v8::Integer,
|
deno_core::v8::Integer,
|
||||||
|
@ -79,28 +77,19 @@ impl send_stdin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let get_class = {
|
let fut = deno_core::_ops::map_async_op1(
|
||||||
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
ctx,
|
||||||
state.tracker.track_async(op_id);
|
Self::call(
|
||||||
state.get_error_class_fn
|
compile_error!("mutable opstate is not supported in async ops"),
|
||||||
};
|
arg_0,
|
||||||
|
),
|
||||||
|
);
|
||||||
let maybe_response = deno_core::_ops::queue_async_op(
|
let maybe_response = deno_core::_ops::queue_async_op(
|
||||||
ctx,
|
ctx,
|
||||||
scope,
|
scope,
|
||||||
false,
|
false,
|
||||||
async move {
|
|
||||||
let result = Self::call(
|
|
||||||
compile_error!("mutable opstate is not supported in async ops"),
|
|
||||||
arg_0,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
promise_id,
|
promise_id,
|
||||||
op_id,
|
fut,
|
||||||
deno_core::_ops::to_op_result(get_class, result),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Some(response) = maybe_response {
|
if let Some(response) = maybe_response {
|
||||||
rv.set(response);
|
rv.set(response);
|
||||||
|
|
|
@ -48,8 +48,6 @@ impl send_stdin {
|
||||||
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
&*(deno_core::v8::Local::<deno_core::v8::External>::cast(args.data()).value()
|
||||||
as *const deno_core::_ops::OpCtx)
|
as *const deno_core::_ops::OpCtx)
|
||||||
};
|
};
|
||||||
let op_id = ctx.id;
|
|
||||||
let realm_idx = ctx.realm_idx;
|
|
||||||
let promise_id = args.get(0);
|
let promise_id = args.get(0);
|
||||||
let promise_id = deno_core::v8::Local::<
|
let promise_id = deno_core::v8::Local::<
|
||||||
deno_core::v8::Integer,
|
deno_core::v8::Integer,
|
||||||
|
@ -77,28 +75,19 @@ impl send_stdin {
|
||||||
return deno_core::_ops::throw_type_error(scope, msg);
|
return deno_core::_ops::throw_type_error(scope, msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let get_class = {
|
let fut = deno_core::_ops::map_async_op1(
|
||||||
let state = ::std::cell::RefCell::borrow(&ctx.state);
|
ctx,
|
||||||
state.tracker.track_async(op_id);
|
Self::call(
|
||||||
state.get_error_class_fn
|
compile_error!("mutable opstate is not supported in async ops"),
|
||||||
};
|
arg_0,
|
||||||
|
),
|
||||||
|
);
|
||||||
let maybe_response = deno_core::_ops::queue_async_op(
|
let maybe_response = deno_core::_ops::queue_async_op(
|
||||||
ctx,
|
ctx,
|
||||||
scope,
|
scope,
|
||||||
false,
|
false,
|
||||||
async move {
|
|
||||||
let result = Self::call(
|
|
||||||
compile_error!("mutable opstate is not supported in async ops"),
|
|
||||||
arg_0,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
(
|
|
||||||
realm_idx,
|
|
||||||
promise_id,
|
promise_id,
|
||||||
op_id,
|
fut,
|
||||||
deno_core::_ops::to_op_result(get_class, result),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
if let Some(response) = maybe_response {
|
if let Some(response) = maybe_response {
|
||||||
rv.set(response);
|
rv.set(response);
|
||||||
|
|
Loading…
Add table
Reference in a new issue