2023-01-02 16:00:42 -05:00
|
|
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
2020-09-05 20:34:02 -04:00
|
|
|
|
2022-09-22 22:55:37 -04:00
|
|
|
use crate::error::AnyError;
|
2020-09-10 09:57:45 -04:00
|
|
|
use crate::gotham_state::GothamState;
|
2020-12-16 11:14:12 -05:00
|
|
|
use crate::resources::ResourceTable;
|
|
|
|
use crate::runtime::GetErrorClassFn;
|
2022-10-21 10:13:42 -04:00
|
|
|
use crate::runtime::JsRuntimeState;
|
2022-04-08 04:32:48 -04:00
|
|
|
use crate::OpDecl;
|
2022-03-14 13:44:15 -04:00
|
|
|
use crate::OpsTracker;
|
2021-11-16 09:02:28 -05:00
|
|
|
use anyhow::Error;
|
2021-10-09 16:37:19 -04:00
|
|
|
use futures::future::MaybeDone;
|
2019-09-30 14:59:44 -04:00
|
|
|
use futures::Future;
|
2023-04-26 14:02:27 -04:00
|
|
|
use futures::FutureExt;
|
|
|
|
use pin_project::pin_project;
|
2021-03-31 10:37:38 -04:00
|
|
|
use serde::Serialize;
|
2022-04-08 04:32:48 -04:00
|
|
|
use std::cell::RefCell;
|
2020-09-05 20:34:02 -04:00
|
|
|
use std::ops::Deref;
|
|
|
|
use std::ops::DerefMut;
|
2019-11-16 19:17:47 -05:00
|
|
|
use std::pin::Pin;
|
2023-03-18 18:30:04 -04:00
|
|
|
use std::ptr::NonNull;
|
2022-04-08 04:32:48 -04:00
|
|
|
use std::rc::Rc;
|
2022-10-21 10:13:42 -04:00
|
|
|
use std::rc::Weak;
|
2023-03-18 18:30:04 -04:00
|
|
|
use v8::fast_api::CFunctionInfo;
|
|
|
|
use v8::fast_api::CTypeInfo;
|
2021-10-09 16:37:19 -04:00
|
|
|
|
2023-04-26 14:02:27 -04:00
|
|
|
pub type RealmIdx = u16;
|
|
|
|
pub type PromiseId = i32;
|
|
|
|
pub type OpId = u16;
|
|
|
|
|
|
|
|
#[pin_project]
|
|
|
|
pub struct OpCall {
|
|
|
|
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>>>>,
|
2022-09-06 13:38:37 -04:00
|
|
|
}
|
|
|
|
|
2023-04-26 14:02:27 -04:00
|
|
|
impl OpCall {
|
2021-10-09 16:37:19 -04:00
|
|
|
/// Wraps a future; the inner future is polled the usual way (lazily).
|
2023-04-26 14:02:27 -04:00
|
|
|
pub fn pending(
|
|
|
|
op_ctx: &OpCtx,
|
|
|
|
promise_id: PromiseId,
|
|
|
|
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),
|
|
|
|
}
|
2021-10-09 16:37:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a future by specifying its output. This is basically the same as
|
|
|
|
/// `async { value }` or `futures::future::ready(value)`.
|
2023-04-26 14:02:27 -04:00
|
|
|
pub fn ready(op_ctx: &OpCtx, promise_id: PromiseId, value: OpResult) -> Self {
|
|
|
|
Self {
|
|
|
|
realm_idx: op_ctx.realm_idx,
|
|
|
|
op_id: op_ctx.id,
|
|
|
|
promise_id,
|
|
|
|
fut: MaybeDone::Done(value),
|
|
|
|
}
|
2021-10-09 16:37:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-26 14:02:27 -04:00
|
|
|
impl Future for OpCall {
|
|
|
|
type Output = (RealmIdx, PromiseId, OpId, OpResult);
|
2021-10-09 16:37:19 -04:00
|
|
|
|
|
|
|
fn poll(
|
|
|
|
self: std::pin::Pin<&mut Self>,
|
|
|
|
cx: &mut std::task::Context<'_>,
|
|
|
|
) -> std::task::Poll<Self::Output> {
|
2023-04-26 14:02:27 -04:00
|
|
|
let realm_idx = self.realm_idx;
|
|
|
|
let promise_id = self.promise_id;
|
|
|
|
let op_id = self.op_id;
|
|
|
|
let fut = &mut *self.project().fut;
|
|
|
|
match fut {
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
.map(move |res| (realm_idx, promise_id, op_id, res))
|
2021-10-09 16:37:19 -04:00
|
|
|
}
|
|
|
|
}
|
2019-09-30 14:59:44 -04:00
|
|
|
|
2021-04-12 17:38:26 -04:00
|
|
|
pub enum OpResult {
|
2021-04-19 09:19:49 -04:00
|
|
|
Ok(serde_v8::SerializablePkg),
|
2021-04-01 07:24:30 -04:00
|
|
|
Err(OpError),
|
|
|
|
}
|
2021-03-31 10:37:38 -04:00
|
|
|
|
2021-04-12 17:38:26 -04:00
|
|
|
impl OpResult {
|
|
|
|
pub fn to_v8<'a>(
|
2022-09-01 06:24:40 -04:00
|
|
|
&mut self,
|
2021-04-12 17:38:26 -04:00
|
|
|
scope: &mut v8::HandleScope<'a>,
|
|
|
|
) -> Result<v8::Local<'a, v8::Value>, serde_v8::Error> {
|
|
|
|
match self {
|
|
|
|
Self::Ok(x) => x.to_v8(scope),
|
|
|
|
Self::Err(err) => serde_v8::to_v8(scope, err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-07 05:25:10 -04:00
|
|
|
#[derive(Debug, Serialize)]
|
2021-03-31 10:37:38 -04:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct OpError {
|
2021-04-01 07:24:30 -04:00
|
|
|
#[serde(rename = "$err_class_name")]
|
2021-03-31 10:37:38 -04:00
|
|
|
class_name: &'static str,
|
|
|
|
message: String,
|
2021-11-04 11:44:34 -04:00
|
|
|
code: Option<&'static str>,
|
2021-03-31 10:37:38 -04:00
|
|
|
}
|
|
|
|
|
2022-03-14 13:44:15 -04:00
|
|
|
impl OpError {
|
|
|
|
pub fn new(get_class: GetErrorClassFn, err: Error) -> Self {
|
|
|
|
Self {
|
|
|
|
class_name: (get_class)(&err),
|
2023-01-27 10:43:16 -05:00
|
|
|
message: format!("{err:#}"),
|
2022-03-14 13:44:15 -04:00
|
|
|
code: crate::error_codes::get_error_code(&err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_op_result<R: Serialize + 'static>(
|
|
|
|
get_class: GetErrorClassFn,
|
2021-11-16 09:02:28 -05:00
|
|
|
result: Result<R, Error>,
|
2021-04-30 10:51:54 -04:00
|
|
|
) -> OpResult {
|
|
|
|
match result {
|
2021-04-12 17:38:26 -04:00
|
|
|
Ok(v) => OpResult::Ok(v.into()),
|
2022-03-14 13:44:15 -04:00
|
|
|
Err(err) => OpResult::Err(OpError::new(get_class, err)),
|
2021-04-30 10:51:54 -04:00
|
|
|
}
|
2021-03-31 10:37:38 -04:00
|
|
|
}
|
|
|
|
|
2022-04-08 04:32:48 -04:00
|
|
|
// TODO(@AaronO): optimize OpCtx(s) mem usage ?
|
|
|
|
pub struct OpCtx {
|
|
|
|
pub id: OpId,
|
|
|
|
pub state: Rc<RefCell<OpState>>,
|
2022-12-26 11:00:13 -05:00
|
|
|
pub decl: Rc<OpDecl>,
|
2023-03-18 18:30:04 -04:00
|
|
|
pub fast_fn_c_info: Option<NonNull<v8::fast_api::CFunctionInfo>>,
|
2022-10-21 10:13:42 -04:00
|
|
|
pub runtime_state: Weak<RefCell<JsRuntimeState>>,
|
2022-12-26 11:00:13 -05:00
|
|
|
// Index of the current realm into `JsRuntimeState::known_realms`.
|
feat(core): Reland support for async ops in realms (#17204)
Currently realms are supported on `deno_core`, but there was no support
for async ops anywhere other than the main realm. The main issue is that
the `js_recv_cb` callback, which resolves promises corresponding to
async ops, was only set for the main realm, so async ops in other realms
would never resolve. Furthermore, promise ID's are specific to each
realm, which meant that async ops from other realms would result in a
wrong promise from the main realm being resolved.
This change takes the `ContextState` struct added in #17050, and adds to
it a `js_recv_cb` callback for each realm. Combined with the fact that
that same PR also added a list of known realms to `JsRuntimeState`, and
that #17174 made `OpCtx` instances realm-specific and had them include
an index into that list of known realms, this makes it possible to know
the current realm in the `queue_async_op` and `queue_fast_async_op`
methods, and therefore to send the results of promises for each realm to
that realm, and prevent the ID's from getting mixed up.
Additionally, since promise ID's are no longer unique to the isolate,
having a single set of unrefed ops doesn't work. This change therefore
also moves `unrefed_ops` from `JsRuntimeState` to `ContextState`, and
adds the lengths of the unrefed op sets for all known realms to get the
total number of unrefed ops to compare in the event loop.
This PR is a reland of #14734 after it was reverted in #16366, except
that `ContextState` and `JsRuntimeState::known_realms` were previously
relanded in #17050. Another significant difference with the original PR
is passing around an index into `JsRuntimeState::known_realms` instead
of a `v8::Global<v8::Context>` to identify the realm, because async op
queuing in fast calls cannot call into V8, and therefore cannot have
access to V8 globals. This also simplified the implementation of
`resolve_async_ops`.
Co-authored-by: Luis Malheiro <luismalheiro@gmail.com>
2023-01-14 08:40:16 -05:00
|
|
|
pub realm_idx: RealmIdx,
|
2022-04-08 04:32:48 -04:00
|
|
|
}
|
|
|
|
|
2023-03-18 18:30:04 -04:00
|
|
|
impl OpCtx {
|
|
|
|
pub fn new(
|
|
|
|
id: OpId,
|
|
|
|
realm_idx: RealmIdx,
|
|
|
|
decl: Rc<OpDecl>,
|
|
|
|
state: Rc<RefCell<OpState>>,
|
|
|
|
runtime_state: Weak<RefCell<JsRuntimeState>>,
|
|
|
|
) -> Self {
|
|
|
|
let mut fast_fn_c_info = None;
|
|
|
|
|
|
|
|
if let Some(fast_fn) = &decl.fast_fn {
|
2023-03-31 08:42:14 -04:00
|
|
|
let args = CTypeInfo::new_from_slice(fast_fn.args);
|
|
|
|
let ret = CTypeInfo::new(fast_fn.return_type);
|
2023-03-18 18:30:04 -04:00
|
|
|
|
|
|
|
// SAFETY: all arguments are coming from the trait and they have
|
|
|
|
// static lifetime
|
|
|
|
let c_fn = unsafe {
|
2023-03-31 08:42:14 -04:00
|
|
|
CFunctionInfo::new(args.as_ptr(), fast_fn.args.len(), ret.as_ptr())
|
2023-03-18 18:30:04 -04:00
|
|
|
};
|
|
|
|
fast_fn_c_info = Some(c_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
OpCtx {
|
|
|
|
id,
|
|
|
|
state,
|
|
|
|
runtime_state,
|
|
|
|
decl,
|
|
|
|
realm_idx,
|
|
|
|
fast_fn_c_info,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-05 20:26:14 -05:00
|
|
|
/// Maintains the resources and ops inside a JS runtime.
|
2020-09-10 09:57:45 -04:00
|
|
|
pub struct OpState {
|
2020-12-16 11:14:12 -05:00
|
|
|
pub resource_table: ResourceTable,
|
|
|
|
pub get_error_class_fn: GetErrorClassFn,
|
2022-03-14 13:44:15 -04:00
|
|
|
pub tracker: OpsTracker,
|
2022-09-22 22:55:37 -04:00
|
|
|
pub last_fast_op_error: Option<AnyError>,
|
2020-09-10 09:57:45 -04:00
|
|
|
gotham_state: GothamState,
|
2019-09-30 14:59:44 -04:00
|
|
|
}
|
|
|
|
|
2021-01-05 16:10:50 -05:00
|
|
|
impl OpState {
|
2022-03-14 13:44:15 -04:00
|
|
|
pub fn new(ops_count: usize) -> OpState {
|
2020-09-10 09:57:45 -04:00
|
|
|
OpState {
|
2020-11-24 18:38:23 -05:00
|
|
|
resource_table: Default::default(),
|
2020-09-10 09:57:45 -04:00
|
|
|
get_error_class_fn: &|_| "Error",
|
2022-03-14 13:44:15 -04:00
|
|
|
gotham_state: Default::default(),
|
2022-09-22 22:55:37 -04:00
|
|
|
last_fast_op_error: None,
|
2022-06-30 18:43:25 -04:00
|
|
|
tracker: OpsTracker::new(ops_count),
|
2020-09-10 09:57:45 -04:00
|
|
|
}
|
2019-09-30 14:59:44 -04:00
|
|
|
}
|
2020-09-10 09:57:45 -04:00
|
|
|
}
|
2019-09-30 14:59:44 -04:00
|
|
|
|
2020-09-10 09:57:45 -04:00
|
|
|
impl Deref for OpState {
|
|
|
|
type Target = GothamState;
|
2020-06-09 18:14:13 -04:00
|
|
|
|
2020-09-10 09:57:45 -04:00
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.gotham_state
|
2020-06-09 18:14:13 -04:00
|
|
|
}
|
2020-09-10 09:57:45 -04:00
|
|
|
}
|
2019-09-30 14:59:44 -04:00
|
|
|
|
2020-09-10 09:57:45 -04:00
|
|
|
impl DerefMut for OpState {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.gotham_state
|
2019-09-30 14:59:44 -04:00
|
|
|
}
|
|
|
|
}
|