Skip to content

Commit 9d0e017

Browse files
committed
Implement calling views
1 parent 8dcdffb commit 9d0e017

File tree

5 files changed

+230
-9
lines changed

5 files changed

+230
-9
lines changed

crates/core/src/host/module_host.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl Instance {
408408
fn call_view(&mut self, tx: MutTxId, params: CallViewParams) -> ViewCallResult {
409409
match self {
410410
Instance::Wasm(inst) => inst.call_view(tx, params),
411-
Instance::Js(_inst) => unimplemented!("JS views are not implemented yet"),
411+
Instance::Js(inst) => inst.call_view(tx, params),
412412
}
413413
}
414414

crates/core/src/host/v8/mod.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ use self::error::{
55
};
66
use self::ser::serialize_to_js;
77
use self::string::{str_from_ident, IntoJsString};
8-
use self::syscall::{call_call_reducer, call_describe_module, get_hooks, resolve_sys_module, FnRet, HookFunctions};
8+
use self::syscall::{
9+
call_call_reducer, call_call_view, call_call_view_anon, call_describe_module, get_hooks, resolve_sys_module, FnRet,
10+
HookFunctions,
11+
};
912
use super::module_common::{build_common_module_from_raw, run_describer, ModuleCommon};
1013
use super::module_host::{CallProcedureParams, CallReducerParams, Module, ModuleInfo, ModuleRuntime};
1114
use super::UpdateDatabaseResult;
1215
use crate::host::instance_env::{ChunkPool, InstanceEnv};
13-
use crate::host::module_host::Instance;
16+
use crate::host::module_host::{CallViewParams, Instance, ViewCallResult};
1417
use crate::host::v8::error::{ErrorOrException, ExceptionThrown};
1518
use crate::host::wasm_common::instrumentation::CallTimes;
1619
use crate::host::wasm_common::module_host_actor::{
17-
DescribeError, ExecutionStats, ExecutionTimings, InstanceCommon, ReducerExecuteResult,
20+
DescribeError, ExecutionStats, ExecutionTimings, InstanceCommon, ReducerExecuteResult, ViewExecuteResult,
1821
};
1922
use crate::host::wasm_common::{RowIters, TimingSpanSet};
2023
use crate::host::{ReducerCallResult, Scheduler};
@@ -25,7 +28,7 @@ use anyhow::Context as _;
2528
use core::str;
2629
use itertools::Either;
2730
use spacetimedb_client_api_messages::energy::FunctionBudget;
28-
use spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId};
31+
use spacetimedb_datastore::locking_tx_datastore::{FuncCallType, MutTxId, ViewCall};
2932
use spacetimedb_datastore::traits::Program;
3033
use spacetimedb_lib::{RawModuleDef, Timestamp};
3134
use spacetimedb_schema::auto_migrate::MigrationPolicy;
@@ -243,6 +246,7 @@ pub struct JsInstance {
243246
request_tx: SyncSender<JsWorkerRequest>,
244247
update_response_rx: Receiver<anyhow::Result<UpdateDatabaseResult>>,
245248
call_reducer_response_rx: Receiver<(ReducerCallResult, bool)>,
249+
call_view_response_rx: Receiver<(ViewCallResult, bool)>,
246250
trapped: bool,
247251
}
248252

@@ -297,6 +301,24 @@ impl JsInstance {
297301
) -> Result<super::ProcedureCallResult, super::ProcedureCallError> {
298302
todo!("JS/TS module procedure support")
299303
}
304+
305+
pub fn call_view(&mut self, tx: MutTxId, params: CallViewParams) -> ViewCallResult {
306+
// Send the request.
307+
let request = JsWorkerRequest::CallView { tx, params };
308+
self.request_tx
309+
.send(request)
310+
.expect("worker's `request_rx` should be live as `JsInstance::drop` hasn't happened");
311+
312+
// Wait for the response.
313+
let (response, trapped) = self
314+
.call_view_response_rx
315+
.recv()
316+
.expect("worker's `call_view_response_tx` should be live as `JsInstance::drop` hasn't happened");
317+
318+
self.trapped = trapped;
319+
320+
response
321+
}
300322
}
301323

302324
/// A request for the worker in [`spawn_instance_worker`].
@@ -315,6 +337,8 @@ enum JsWorkerRequest {
315337
tx: Option<MutTxId>,
316338
params: CallReducerParams,
317339
},
340+
/// See [`JsInstance::call_view`].
341+
CallView { tx: MutTxId, params: CallViewParams },
318342
}
319343

320344
/// Performs some of the startup work of [`spawn_instance_worker`].
@@ -376,6 +400,8 @@ fn spawn_instance_worker(
376400
let (update_response_tx, update_response_rx) = mpsc::sync_channel(0);
377401
// The Worker --ReducerCallResult-> Instance channel:
378402
let (call_reducer_response_tx, call_reducer_response_rx) = mpsc::sync_channel(0);
403+
// The Worker --ViewCallResult-> Instance channel:
404+
let (call_view_response_tx, call_view_response_rx) = mpsc::sync_channel(0);
379405

380406
// This one-shot channel is used for initial startup error handling within the thread.
381407
let (result_tx, result_rx) = oneshot::channel();
@@ -449,6 +475,13 @@ fn spawn_instance_worker(
449475
unreachable!("should have receiver for `call_reducer` response, {e}");
450476
}
451477
}
478+
JsWorkerRequest::CallView { tx, params } => {
479+
let res = call_view(&mut instance_common, replica_ctx, scope, &hooks, tx, params);
480+
481+
if let Err(e) = call_view_response_tx.send(res) {
482+
unreachable!("should have receiver for `call_view` response, {e}");
483+
}
484+
}
452485
}
453486
}
454487
});
@@ -460,6 +493,7 @@ fn spawn_instance_worker(
460493
request_tx,
461494
update_response_rx,
462495
call_reducer_response_rx,
496+
call_view_response_rx,
463497
trapped: false,
464498
};
465499
(opt_mc, inst)
@@ -655,6 +689,43 @@ fn call_reducer<'scope>(
655689
(res, trapped)
656690
}
657691

692+
fn call_view<'scope>(
693+
instance_common: &mut InstanceCommon,
694+
replica_ctx: &ReplicaContext,
695+
scope: &mut PinScope<'scope, '_>,
696+
hooks: &HookFunctions<'_>,
697+
tx: MutTxId,
698+
params: CallViewParams,
699+
) -> (ViewCallResult, bool) {
700+
let mut trapped = false;
701+
702+
let is_anonymous = params.is_anonymous;
703+
let (res, _) = instance_common.call_view_with_tx(
704+
replica_ctx,
705+
tx,
706+
params,
707+
move |a, b, c| log_traceback(replica_ctx, a, b, c),
708+
|tx, op, budget| {
709+
let func = FuncCallType::View(if is_anonymous {
710+
ViewCall::anonymous(op.db_id, op.args.get_bsatn().clone())
711+
} else {
712+
ViewCall::with_identity(*op.caller_identity, op.db_id, op.args.get_bsatn().clone())
713+
});
714+
let (tx, stats, call_result) =
715+
common_call(scope, tx, op.name, op.timestamp, budget, func, &mut trapped, |scope| {
716+
Ok(if is_anonymous {
717+
call_call_view_anon(scope, hooks, op.into())?
718+
} else {
719+
call_call_view(scope, hooks, op)?
720+
})
721+
});
722+
(tx, ViewExecuteResult { stats, call_result })
723+
},
724+
);
725+
726+
(res, trapped)
727+
}
728+
658729
/// Extracts the raw module def by running the registered `__describe_module__` hook.
659730
fn extract_description<'scope>(
660731
scope: &mut PinScope<'scope, '_>,

crates/core/src/host/v8/syscall/hooks.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub(super) fn set_hook_slots(
4848
pub(in super::super) enum ModuleHookKey {
4949
DescribeModule,
5050
CallReducer,
51+
CallView,
52+
CallAnonymousView,
5153
}
5254

5355
impl ModuleHookKey {
@@ -59,6 +61,8 @@ impl ModuleHookKey {
5961
// reverted to just 0, 1... once denoland/rusty_v8#1868 merges
6062
ModuleHookKey::DescribeModule => 20,
6163
ModuleHookKey::CallReducer => 21,
64+
ModuleHookKey::CallView => 22,
65+
ModuleHookKey::CallAnonymousView => 23,
6266
}
6367
}
6468
}
@@ -104,6 +108,8 @@ pub(in super::super) struct HookFunctions<'scope> {
104108
/// describe_module and call_reducer existed in v1.0, but everything else is `Option`al
105109
pub describe_module: Local<'scope, Function>,
106110
pub call_reducer: Local<'scope, Function>,
111+
pub call_view: Option<Local<'scope, Function>>,
112+
pub call_view_anon: Option<Local<'scope, Function>>,
107113
}
108114

109115
/// Returns the hook function previously registered in [`register_hooks`].
@@ -123,5 +129,7 @@ pub(in super::super) fn get_hooks<'scope>(scope: &mut PinScope<'scope, '_>) -> O
123129
abi: hooks.abi,
124130
describe_module: get(ModuleHookKey::DescribeModule)?,
125131
call_reducer: get(ModuleHookKey::CallReducer)?,
132+
call_view: get(ModuleHookKey::CallView),
133+
call_view_anon: get(ModuleHookKey::CallAnonymousView),
126134
})
127135
}

crates/core/src/host/v8/syscall/mod.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
use bytes::Bytes;
12
use spacetimedb_lib::{RawModuleDef, VersionTuple};
23
use v8::{callback_scope, Context, FixedArray, Local, Module, PinScope};
34

45
use crate::host::v8::de::scratch_buf;
56
use crate::host::v8::error::{ErrorOrException, ExcResult, ExceptionThrown, Throwable, TypeError};
67
use crate::host::wasm_common::abi::parse_abi_version;
7-
use crate::host::wasm_common::module_host_actor::{ReducerOp, ReducerResult};
8+
use crate::host::wasm_common::module_host_actor::{AnonymousViewOp, ReducerOp, ReducerResult, ViewOp};
89

910
mod hooks;
1011
mod v1;
@@ -52,6 +53,7 @@ fn resolve_sys_module_inner<'scope>(
5253
match module {
5354
"sys" => match (major, minor) {
5455
(1, 0) => Ok(v1::sys_v1_0(scope)),
56+
(1, 1) => Ok(v1::sys_v1_1(scope)),
5557
_ => Err(TypeError(format!(
5658
"Could not import {spec:?}, likely because this module was built for a newer version of SpacetimeDB.\n\
5759
It requires sys module v{major}.{minor}, but that version is not supported by the database."
@@ -75,6 +77,32 @@ pub(super) fn call_call_reducer(
7577
}
7678
}
7779

80+
/// Calls the registered `__call_view__` function hook.
81+
///
82+
/// This handles any (future) ABI version differences.
83+
pub(super) fn call_call_view(
84+
scope: &mut PinScope<'_, '_>,
85+
hooks: &HookFunctions<'_>,
86+
op: ViewOp<'_>,
87+
) -> Result<Bytes, ErrorOrException<ExceptionThrown>> {
88+
match hooks.abi {
89+
AbiVersion::V1 => v1::call_call_view(scope, hooks, op),
90+
}
91+
}
92+
93+
/// Calls the registered `__call_view_anon__` function hook.
94+
///
95+
/// This handles any (future) ABI version differences.
96+
pub(super) fn call_call_view_anon(
97+
scope: &mut PinScope<'_, '_>,
98+
hooks: &HookFunctions<'_>,
99+
op: AnonymousViewOp<'_>,
100+
) -> Result<Bytes, ErrorOrException<ExceptionThrown>> {
101+
match hooks.abi {
102+
AbiVersion::V1 => v1::call_call_view_anon(scope, hooks, op),
103+
}
104+
}
105+
78106
/// Calls the registered `__describe_module__` function hook.
79107
///
80108
/// This handles any (future) ABI version differences.

0 commit comments

Comments
 (0)