1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::{
56 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
57};
58use crate::fiber::{self, StoreFiber, StoreFiberYield};
59use crate::hash_set::HashSet;
60#[cfg(feature = "gc")]
61use crate::module::ModuleRegistry;
62use crate::prelude::*;
63use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
64#[cfg(feature = "gc")]
65use crate::vm::GcRootsList;
66use crate::vm::component::{CallContext, ComponentInstance, CurrentScope, InstanceState, Scope};
67use crate::vm::{
68 AlwaysMut, SendSyncPtr, UncaughtException, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore,
69};
70use crate::{AsContext, AsContextMut, Result, StoreContext, StoreContextMut, ValRaw, bail};
71use crate::{Instance as ModuleInstance, bail_bug};
72use alloc::borrow::ToOwned;
73use alloc::collections::btree_map::Entry;
74use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
75use core::any::Any;
76use core::cell::UnsafeCell;
77use core::fmt;
78use core::future;
79use core::future::Future;
80use core::marker::PhantomData;
81use core::mem::{self, ManuallyDrop, MaybeUninit};
82use core::ops::DerefMut;
83use core::pin::{Pin, pin};
84use core::ptr::{self, NonNull};
85use core::task::{Context, Poll, Waker};
86use futures::channel::oneshot;
87use futures::stream::{FuturesUnordered, StreamExt};
88use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
89use table::{TableDebug, TableId};
90use wasmtime_environ::component::{
91 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
92 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
93 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
94 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
95 TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
96};
97use wasmtime_environ::packed_option::ReservedValue;
98use wasmtime_environ::{ModuleInternedTypeIndex, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
99#[cfg(feature = "gc")]
100use wasmtime_unwinder::Unwind;
101
102pub use abort::JoinHandle;
103pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
104pub use future_stream_any::{FutureAny, StreamAny};
105pub use futures_and_streams::{
106 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
107 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
108 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
109};
110pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
111#[cfg(feature = "task-group-hook")]
112pub use task_group_hook::TaskGroupHook;
113pub use task_group_hook::TaskGroupId;
114
115mod abort;
116mod error_contexts;
117mod func;
118mod future_stream_any;
119mod futures_and_streams;
120pub(crate) mod table;
121#[cfg(feature = "task-group-hook")]
122mod task_group_hook;
123#[cfg(not(feature = "task-group-hook"))]
124mod task_group_hook_disabled;
125#[cfg(not(feature = "task-group-hook"))]
126use task_group_hook_disabled as task_group_hook;
127pub(crate) mod tls;
128
129const BLOCKED: u32 = 0xffff_ffff;
132
133#[derive(Clone, Copy, Eq, PartialEq, Debug)]
135pub enum Status {
136 Starting = 0,
137 Started = 1,
138 Returned = 2,
139 StartCancelled = 3,
140 ReturnCancelled = 4,
141}
142
143impl Status {
144 pub fn pack(self, waitable: Option<u32>) -> u32 {
150 assert!(matches!(self, Status::Returned) == waitable.is_none());
151 let waitable = waitable.unwrap_or(0);
152 assert!(waitable < (1 << 28));
153 (waitable << 4) | (self as u32)
154 }
155}
156
157#[derive(Clone, Copy, Debug)]
160enum Event {
161 None,
162 Subtask {
163 status: Status,
164 },
165 StreamRead {
166 code: ReturnCode,
167 pending: Option<(TypeStreamTableIndex, u32)>,
168 },
169 StreamWrite {
170 code: ReturnCode,
171 pending: Option<(TypeStreamTableIndex, u32)>,
172 },
173 FutureRead {
174 code: ReturnCode,
175 pending: Option<(TypeFutureTableIndex, u32)>,
176 },
177 FutureWrite {
178 code: ReturnCode,
179 pending: Option<(TypeFutureTableIndex, u32)>,
180 },
181 Cancelled,
182}
183
184impl Event {
185 fn parts(self) -> (u32, u32) {
190 const EVENT_NONE: u32 = 0;
191 const EVENT_SUBTASK: u32 = 1;
192 const EVENT_STREAM_READ: u32 = 2;
193 const EVENT_STREAM_WRITE: u32 = 3;
194 const EVENT_FUTURE_READ: u32 = 4;
195 const EVENT_FUTURE_WRITE: u32 = 5;
196 const EVENT_CANCELLED: u32 = 6;
197 match self {
198 Event::None => (EVENT_NONE, 0),
199 Event::Cancelled => (EVENT_CANCELLED, 0),
200 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
201 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
202 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
203 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
204 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
205 }
206 }
207}
208
209mod callback_code {
211 pub const EXIT: u32 = 0;
212 pub const YIELD: u32 = 1;
213 pub const WAIT: u32 = 2;
214}
215
216const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
218const START_FLAG_ASYNC_CALLER: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLER as u32;
219
220pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
226 store: StoreContextMut<'a, T>,
227 get_data: fn(&mut T) -> D::Data<'_>,
228}
229
230impl<'a, T, D> Access<'a, T, D>
231where
232 D: HasData + ?Sized,
233 T: 'static,
234{
235 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
237 Self { store, get_data }
238 }
239
240 pub fn data_mut(&mut self) -> &mut T {
242 self.store.data_mut()
243 }
244
245 pub fn get(&mut self) -> D::Data<'_> {
247 (self.get_data)(self.data_mut())
248 }
249
250 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
254 where
255 T: 'static,
256 {
257 let accessor = Accessor {
258 get_data: self.get_data,
259 token: StoreToken::new(self.store.as_context_mut()),
260 };
261 self.store
262 .as_context_mut()
263 .spawn_with_accessor(accessor, task)
264 }
265
266 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
269 self.get_data
270 }
271}
272
273impl<'a, T, D> AsContext for Access<'a, T, D>
274where
275 D: HasData + ?Sized,
276 T: 'static,
277{
278 type Data = T;
279
280 fn as_context(&self) -> StoreContext<'_, T> {
281 self.store.as_context()
282 }
283}
284
285impl<'a, T, D> AsContextMut for Access<'a, T, D>
286where
287 D: HasData + ?Sized,
288 T: 'static,
289{
290 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
291 self.store.as_context_mut()
292 }
293}
294
295pub struct Accessor<T: 'static, D = HasSelf<T>>
355where
356 D: HasData + ?Sized,
357{
358 token: StoreToken<T>,
359 get_data: fn(&mut T) -> D::Data<'_>,
360}
361
362pub trait AsAccessor {
379 type Data: 'static;
381
382 type AccessorData: HasData + ?Sized;
385
386 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
388}
389
390impl<T: AsAccessor + ?Sized> AsAccessor for &T {
391 type Data = T::Data;
392 type AccessorData = T::AccessorData;
393
394 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
395 T::as_accessor(self)
396 }
397}
398
399impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
400 type Data = T;
401 type AccessorData = D;
402
403 fn as_accessor(&self) -> &Accessor<T, D> {
404 self
405 }
406}
407
408const _: () = {
431 const fn assert<T: Send + Sync>() {}
432 assert::<Accessor<UnsafeCell<u32>>>();
433};
434
435impl<T> Accessor<T> {
436 pub(crate) fn new(token: StoreToken<T>) -> Self {
445 Self {
446 token,
447 get_data: |x| x,
448 }
449 }
450}
451
452impl<T, D> Accessor<T, D>
453where
454 D: HasData + ?Sized,
455{
456 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
474 tls::get(|vmstore| {
475 fun(Access {
476 store: self.token.as_context_mut(vmstore),
477 get_data: self.get_data,
478 })
479 })
480 }
481
482 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
485 self.get_data
486 }
487
488 pub fn with_getter<D2: HasData>(
505 &self,
506 get_data: fn(&mut T) -> D2::Data<'_>,
507 ) -> Accessor<T, D2> {
508 Accessor {
509 token: self.token,
510 get_data,
511 }
512 }
513
514 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
530 where
531 T: 'static,
532 {
533 let accessor = self.clone_for_spawn();
534 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
535 }
536
537 fn clone_for_spawn(&self) -> Self {
538 Self {
539 token: self.token,
540 get_data: self.get_data,
541 }
542 }
543
544 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
580 self.with(|mut access| {
581 let store = access.as_context_mut().0;
582 let state = store.concurrent_state_mut_without_forcing_current_thread();
583 if state.interesting_tasks == 0 {
584 Poll::Ready(())
585 } else {
586 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
587 Poll::Pending
588 }
589 })
590 }
591
592 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
609 self.with(|mut access| {
610 let store = access.as_context_mut().0;
611 let (_, _, _, raw_options) = func.abi_info(store);
612 let instance = func.instance().runtime_instance(raw_options.instance);
613 let state = store.instance_state(instance).concurrent_state();
614 if state.backpressure == 0 {
615 Poll::Ready(())
616 } else {
617 store
618 .concurrent_state_mut_without_forcing_current_thread()
619 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
620 Poll::Pending
621 }
622 })
623 }
624}
625
626pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
648 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
649where
650 D: HasData + ?Sized,
651{
652 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
654}
655
656impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
657where
658 T: 'static,
659 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
660 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
661 Fut: Future<Output = Result<()>> + Send + 'fut,
662 D: HasData,
663{
664 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
665 (self)(accessor)
666 }
667}
668
669enum CallerInfo {
672 Async {
674 params: Vec<ValRaw>,
675 has_result: bool,
676 },
677 Sync {
679 params: Vec<ValRaw>,
680 result_count: u32,
681 },
682}
683
684enum WaitMode {
686 Fiber(StoreFiber<'static>),
688 Callback(Instance),
691}
692
693impl fmt::Debug for WaitMode {
694 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695 match self {
696 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
697 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
698 }
699 }
700}
701
702#[derive(Debug)]
704enum SuspendReason {
705 Waiting {
708 set: TableId<WaitableSet>,
709 thread: QualifiedThreadId,
710 },
711 YieldingToSubtask { thread: QualifiedThreadId },
714 NeedWork,
717 Yielding { thread: QualifiedThreadId },
720 ExplicitlySuspending { thread: QualifiedThreadId },
723}
724
725enum GuestCallKind {
727 DeliverEvent {
730 instance: Instance,
732 set: Option<TableId<WaitableSet>>,
737 },
738 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
744 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
745}
746
747impl fmt::Debug for GuestCallKind {
748 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
749 match self {
750 Self::DeliverEvent { instance, set } => f
751 .debug_struct("DeliverEvent")
752 .field("instance", instance)
753 .field("set", set)
754 .finish(),
755 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
756 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
757 }
758 }
759}
760
761#[derive(Copy, Clone, Debug)]
763pub enum SuspensionTarget {
764 Resume(u32),
765 Promote(u32),
766 None,
767}
768
769#[derive(Copy, Clone, Debug)]
771pub enum ResumeThread {
772 Promote,
773 Resume,
774 ResumeLater,
775}
776
777#[derive(Debug)]
779struct GuestCall {
780 thread: QualifiedThreadId,
781 kind: GuestCallKind,
782}
783
784impl GuestCall {
785 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
795 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
796 let async_typed = task.async_typed;
797 let instance = task.instance;
798 let state = store.instance_state(instance).concurrent_state();
799
800 let ready = match &self.kind {
801 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
802 GuestCallKind::StartImplicit(_) => {
803 !async_typed || !(state.do_not_enter || state.backpressure > 0)
804 }
805 GuestCallKind::StartExplicit(_) => true,
806 };
807 log::trace!(
808 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
809 state.do_not_enter,
810 state.backpressure
811 );
812 Ok(ready)
813 }
814}
815
816enum WorkerItem {
818 GuestCall(GuestCall),
819 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
820}
821
822enum WorkItem {
825 PushFuture(AlwaysMut<HostTaskFuture>),
827 ResumeFiber {
829 instance: RuntimeInstance,
830 thread: QualifiedThreadId,
831 fiber: StoreFiber<'static>,
832 },
833 ResumeThread {
835 instance: RuntimeInstance,
836 thread: QualifiedThreadId,
837 },
838 GuestCall {
840 instance: RuntimeInstance,
841 call: GuestCall,
842 },
843 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
845}
846
847struct ResumingFiber<'a> {
853 store: &'a mut StoreOpaque,
854 fiber: Option<StoreFiber<'static>>,
855}
856
857impl Drop for ResumingFiber<'_> {
858 fn drop(&mut self) {
859 if let Some(mut fiber) = self.fiber.take() {
860 fiber.dispose(self.store);
861 }
862 }
863}
864
865impl fmt::Debug for WorkItem {
866 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
867 match self {
868 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
869 Self::ResumeFiber {
870 instance, thread, ..
871 } => f
872 .debug_struct("ResumeFiber")
873 .field("instance", instance)
874 .field("thread", thread)
875 .finish(),
876 Self::ResumeThread { instance, thread } => f
877 .debug_struct("ResumeThread")
878 .field("instance", instance)
879 .field("thread", thread)
880 .finish(),
881 Self::GuestCall { instance, call } => f
882 .debug_struct("GuestCall")
883 .field("instance", instance)
884 .field("call", call)
885 .finish(),
886 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
887 }
888 }
889}
890
891#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
893pub(crate) enum WaitResult {
894 Cancelled,
895 Completed,
896}
897
898pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
906 store: &mut dyn VMStore,
907 host_task: EnteredHostTask,
908 future: impl Future<Output = Result<R>> + Send + 'static,
909) -> Result<R> {
910 let mut future = Box::pin(future);
917 let poll = tls::set(store, || {
918 future
919 .as_mut()
920 .poll(&mut Context::from_waker(&Waker::noop()))
921 });
922
923 let caller = match host_task {
924 Some(caller) => caller,
925 None => bail_bug!("host task wasn't created but should have been"),
926 };
927
928 let task = match poll {
929 Poll::Ready(result) => return result,
931
932 Poll::Pending => {
937 let Some(task) = store.materialize_host_task_id()? else {
938 bail_bug!("current thread is not a host thread")
939 };
940
941 let future = Box::pin(async move {
944 let result = run_with_host_task_set(task, future).await??;
945 tls::get(move |store| {
946 let state = store.concurrent_state_mut()?;
947 let host_state = &mut state.get_mut(task)?.state;
948 assert!(matches!(host_state, HostTaskState::CalleeStarted));
949 *host_state = HostTaskState::CalleeFinished(Box::new(result));
950
951 Waitable::Host(task).set_event(
952 state,
953 Some(Event::Subtask {
954 status: Status::Returned,
955 }),
956 )?;
957
958 Ok(())
959 })
960 }) as HostTaskFuture;
961
962 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
963 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
964
965 let state = store.concurrent_state_mut()?;
966 state.push_future(future);
967
968 let set = state.get_mut(caller.thread)?.sync_call_set;
969 Waitable::Host(task).join(state, Some(set))?;
970
971 store.suspend(SuspendReason::Waiting {
972 set,
973 thread: caller,
974 })?;
975
976 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
980 task
981 }
982 };
983
984 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
986 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
987 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
988 Ok(result) => *result,
989 Err(_) => bail_bug!("host task finished with wrong type of result"),
990 }),
991 _ => bail_bug!("unexpected host task state after completion"),
992 }
993}
994
995fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
997 match call.kind {
998 GuestCallKind::DeliverEvent { instance, set } => {
999 if let Some(set) = set {
1002 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1003 }
1004 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1005 Some(pair) => pair,
1006 None => match set {
1007 Some(set) => {
1012 log::trace!(
1013 "event for {:?} on {set:?} no longer present; waiting again",
1014 call.thread
1015 );
1016 return instance.wait_with_callback(
1017 store.store_opaque_mut(),
1018 call.thread,
1019 set,
1020 );
1021 }
1022 None => (Event::None, None),
1027 },
1028 };
1029 let state = store.concurrent_state_mut()?;
1030 if let Some(set) = set
1035 && waitable.is_none()
1036 && !state.get_mut(set)?.ready.is_empty()
1037 {
1038 state.wake_waiter(set)?;
1039 }
1040 let task = state.get_mut(call.thread.task)?;
1041 let runtime_instance = task.instance;
1042 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1043
1044 log::trace!(
1045 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1046 call.thread,
1047 );
1048
1049 let old_thread = store.set_thread(call.thread)?;
1050 log::trace!(
1051 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
1052 call.thread
1053 );
1054
1055 store.enter_instance(runtime_instance);
1056
1057 let Some(callback) = store
1058 .concurrent_state_mut()?
1059 .get_mut(call.thread.task)?
1060 .callback
1061 .take()
1062 else {
1063 bail_bug!("guest task callback field not present")
1064 };
1065
1066 let code = callback(store, event, handle)?;
1067
1068 store
1069 .concurrent_state_mut()?
1070 .get_mut(call.thread.task)?
1071 .callback = Some(callback);
1072
1073 store.exit_instance(runtime_instance)?;
1074
1075 store.set_thread(old_thread)?;
1076
1077 instance.handle_callback_code(store, call.thread, runtime_instance.index, code)?;
1078
1079 log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
1080 }
1081 GuestCallKind::StartImplicit(fun) => {
1082 fun(store)?;
1083 }
1084 GuestCallKind::StartExplicit(fun) => {
1085 fun(store)?;
1086 }
1087 }
1088
1089 Ok(())
1090}
1091
1092impl<T> Store<T> {
1093 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1095 where
1096 T: Send + 'static,
1097 {
1098 ensure!(
1099 self.as_context().0.concurrency_support(),
1100 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1101 );
1102 self.as_context_mut().run_concurrent(fun).await
1103 }
1104
1105 #[doc(hidden)]
1106 pub fn assert_concurrent_state_empty(&mut self) {
1107 self.as_context_mut().assert_concurrent_state_empty();
1108 }
1109
1110 #[doc(hidden)]
1111 pub fn concurrent_state_table_size(&mut self) -> usize {
1112 self.as_context_mut().concurrent_state_table_size()
1113 }
1114
1115 pub fn spawn(
1117 &mut self,
1118 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1119 ) -> Result<JoinHandle>
1120 where
1121 T: 'static,
1122 {
1123 self.as_context_mut().spawn(task)
1124 }
1125}
1126
1127impl<T> StoreContextMut<'_, T> {
1128 #[doc(hidden)]
1139 pub fn assert_concurrent_state_empty(self) {
1140 let store = self.0;
1141 store
1142 .store_data_mut()
1143 .components
1144 .assert_instance_states_empty();
1145 let state = store.concurrent_state_mut().unwrap();
1146 assert!(
1147 state.table.get_mut().is_empty(),
1148 "non-empty table: {:?}",
1149 state.table.get_mut()
1150 );
1151 assert!(state.switch_item.is_none());
1152 assert!(state.next_switch_item.is_none());
1153 assert!(state.high_priority.is_empty());
1154 assert!(state.low_priority.is_empty());
1155 assert!(state.saved_next_switch_items.is_empty());
1156 assert!(state.unforced_current_thread.is_none());
1157 assert!(state.deferred_host_call_context.is_none());
1158 assert!(state.futures_mut().unwrap().is_empty());
1159 assert!(state.global_error_context_ref_counts.is_empty());
1160 }
1161
1162 #[doc(hidden)]
1167 pub fn concurrent_state_table_size(&mut self) -> usize {
1168 self.0
1169 .concurrent_state_mut()
1170 .unwrap()
1171 .table
1172 .get_mut()
1173 .iter_mut()
1174 .count()
1175 }
1176
1177 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1187 where
1188 T: 'static,
1189 {
1190 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1191 self.spawn_with_accessor(accessor, task)
1192 }
1193
1194 fn spawn_with_accessor<D>(
1197 self,
1198 accessor: Accessor<T, D>,
1199 task: impl for<'fut> AccessorTask<'fut, T, D>,
1200 ) -> Result<JoinHandle>
1201 where
1202 T: 'static,
1203 D: HasData + ?Sized,
1204 {
1205 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1209 self.0
1210 .concurrent_state_mut()?
1211 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1212 Ok(handle)
1213 }
1214
1215 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1299 where
1300 T: Send + 'static,
1301 {
1302 ensure!(
1303 self.0.concurrency_support(),
1304 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1305 );
1306 self.do_run_concurrent(fun, false).await
1307 }
1308
1309 pub(super) async fn run_concurrent_trap_on_idle<R>(
1310 self,
1311 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1312 ) -> Result<R> {
1313 self.do_run_concurrent(fun, true).await
1314 }
1315
1316 async fn do_run_concurrent<R>(
1317 mut self,
1318 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1319 trap_on_idle: bool,
1320 ) -> Result<R> {
1321 debug_assert!(self.0.concurrency_support());
1322 let already_running = self
1323 .0
1324 .concurrent_state_mut_already_forced_current_thread()
1325 .event_loop_running;
1326 if already_running {
1327 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1328 }
1329 let token = StoreToken::new(self.as_context_mut());
1330
1331 struct Dropper<'a, T: 'static, V> {
1332 store: StoreContextMut<'a, T>,
1333 value: ManuallyDrop<V>,
1334 }
1335
1336 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1337 fn drop(&mut self) {
1338 self.store
1339 .0
1340 .concurrent_state_mut_already_forced_current_thread()
1341 .event_loop_running = false;
1342
1343 tls::set(self.store.0, || {
1344 unsafe { ManuallyDrop::drop(&mut self.value) }
1349 });
1350 }
1351 }
1352
1353 let accessor = &Accessor::new(token);
1354 self.0
1355 .concurrent_state_mut_already_forced_current_thread()
1356 .event_loop_running = true;
1357 let dropper = &mut Dropper {
1358 store: self,
1359 value: ManuallyDrop::new(fun(accessor)),
1360 };
1361 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1363
1364 let result = dropper
1365 .store
1366 .as_context_mut()
1367 .poll_until(future, trap_on_idle)
1368 .await;
1369
1370 if result.is_err() {
1371 dropper.store.0.set_trapped();
1372 }
1373
1374 result
1375 }
1376
1377 async fn poll_until<R>(
1383 mut self,
1384 mut future: Pin<&mut impl Future<Output = R>>,
1385 trap_on_idle: bool,
1386 ) -> Result<R> {
1387 struct Reset<'a, T: 'static> {
1388 store: StoreContextMut<'a, T>,
1389 futures: Option<FuturesUnordered<HostTaskFuture>>,
1390 }
1391
1392 impl<'a, T> Drop for Reset<'a, T> {
1393 fn drop(&mut self) {
1394 if let Some(futures) = self.futures.take() {
1395 *self
1396 .store
1397 .0
1398 .concurrent_state_mut_already_forced_current_thread()
1399 .futures
1400 .get_mut() = Some(futures);
1401 }
1402 }
1403 }
1404
1405 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1409 let mut turns_without_yield = 0;
1410
1411 loop {
1412 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1416 let mut reset = Reset {
1417 store: self.as_context_mut(),
1418 futures,
1419 };
1420 let mut next = match reset.futures.as_mut() {
1421 Some(f) => pin!(f.next()),
1422 None => bail_bug!("concurrent state missing futures field"),
1423 };
1424
1425 enum PollResult<R> {
1426 Complete(R),
1427 ProcessWork {
1428 ready: Option<WorkItem>,
1429 low_priority: bool,
1430 },
1431 }
1432
1433 let result = future::poll_fn(|cx| {
1434 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1437 return Poll::Ready(Ok(PollResult::Complete(value)));
1438 }
1439
1440 if reset.store.0.trapped() {
1449 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1450 }
1451
1452 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1456 Poll::Ready(Some(output)) => {
1457 match output {
1458 Err(e) => return Poll::Ready(Err(e)),
1459 Ok(()) => {}
1460 }
1461 Poll::Ready(true)
1462 }
1463 Poll::Ready(None) => Poll::Ready(false),
1464 Poll::Pending => Poll::Pending,
1465 };
1466
1467 let state = reset.store.0.concurrent_state_mut()?;
1482 let mut ready = state.switch_item.take();
1483 let mut low_priority = false;
1484 if ready.is_none() {
1485 ready = state.high_priority.pop_back();
1486 if ready.is_none() {
1487 ready = state.low_priority.pop_back();
1488 low_priority = true;
1489 }
1490 }
1491 if ready.is_some() {
1492 return Poll::Ready(Ok(PollResult::ProcessWork {
1493 ready,
1494 low_priority,
1495 }));
1496 }
1497
1498 return match next {
1502 Poll::Ready(true) => {
1503 Poll::Ready(Ok(PollResult::ProcessWork {
1509 ready: None,
1510 low_priority: false,
1511 }))
1512 }
1513 Poll::Ready(false) => {
1514 if let Poll::Ready(value) =
1518 tls::set(reset.store.0, || future.as_mut().poll(cx))
1519 {
1520 Poll::Ready(Ok(PollResult::Complete(value)))
1521 } else {
1522 if trap_on_idle {
1528 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1535 Trap::CannotBlockSyncTask.into()
1536 } else {
1537 Trap::AsyncDeadlock.into()
1539 }))
1540 } else {
1541 Poll::Pending
1545 }
1546 }
1547 }
1548 Poll::Pending => Poll::Pending,
1553 };
1554 })
1555 .await;
1556
1557 drop(reset);
1561
1562 match result? {
1563 PollResult::Complete(value) => break Ok(value),
1566 PollResult::ProcessWork {
1569 ready,
1570 low_priority,
1571 } => {
1572 struct Dispose<'a, T: 'static> {
1573 store: StoreContextMut<'a, T>,
1574 ready: Option<WorkItem>,
1575 }
1576
1577 impl<'a, T> Drop for Dispose<'a, T> {
1578 fn drop(&mut self) {
1579 if let Some(item) = self.ready.take() {
1580 match item {
1581 WorkItem::ResumeFiber { mut fiber, .. } => {
1582 fiber.dispose(self.store.0);
1583 }
1584 WorkItem::PushFuture(future) => {
1585 tls::set(self.store.0, move || drop(future))
1586 }
1587 _ => {}
1588 }
1589 }
1590 }
1591 }
1592
1593 let mut dispose = Dispose {
1594 store: self.as_context_mut(),
1595 ready,
1596 };
1597
1598 if low_priority {
1620 dispose.store.0.yield_now().await;
1621 turns_without_yield = 0;
1622 }
1623
1624 if let Some(item) = dispose.ready.take() {
1625 dispose
1626 .store
1627 .as_context_mut()
1628 .handle_work_item(item)
1629 .await?;
1630 }
1631
1632 turns_without_yield += 1;
1633 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1634 turns_without_yield = 0;
1635 dispose.store.0.yield_now().await;
1636 }
1637 }
1638 }
1639 }
1640 }
1641
1642 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1644 let item = match item {
1648 WorkItem::ResumeFiber {
1649 instance,
1650 thread,
1651 fiber,
1652 } => {
1653 let future = self.0.resume_fiber(fiber);
1654 log::trace!(
1655 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1656 );
1657 return futures::future::Either::Left(future);
1658 }
1659 item => item,
1660 };
1661 futures::future::Either::Right(async move {
1662 log::trace!("handle work item {item:?}");
1663 match item {
1664 WorkItem::PushFuture(future) => {
1665 self.0
1666 .concurrent_state_mut()?
1667 .futures_mut()?
1668 .push(future.into_inner());
1669 }
1670 WorkItem::ResumeFiber { .. } => unreachable!(),
1671 WorkItem::ResumeThread { thread, .. } => {
1672 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1673 if !matches!(slot, GuestThreadState::Ready { .. }) {
1674 bail_bug!("cannot resume non-pending thread {thread:?}");
1675 }
1676 let GuestThreadState::Ready { fiber } =
1677 mem::replace(slot, GuestThreadState::Running)
1678 else {
1679 unreachable!()
1680 };
1681 self.0.resume_fiber(fiber).await?;
1682 }
1683 WorkItem::GuestCall { call, .. } => {
1684 if call.is_ready(self.0)? {
1685 self.0
1686 .concurrent_state_mut()?
1687 .get_mut(call.thread.thread)?
1688 .wake_on_cancel = WakeOnCancel::None;
1689 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1690 } else {
1691 let state = self.0.concurrent_state_mut()?;
1692 let task = state.get_mut(call.thread.task)?;
1693 if !task.starting_sent {
1694 task.starting_sent = true;
1695 if let GuestCallKind::StartImplicit(_) = &call.kind {
1696 Waitable::Guest(call.thread.task).set_event(
1697 state,
1698 Some(Event::Subtask {
1699 status: Status::Starting,
1700 }),
1701 )?;
1702 }
1703 }
1704
1705 let instance = state.get_mut(call.thread.task)?.instance;
1706 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1715 let state = self.0.concurrent_state_mut()?;
1716 if !state.get_mut(*set)?.ready.is_empty() {
1717 state.wake_waiter(*set)?;
1718 }
1719 }
1720 self.0
1721 .instance_state(instance)
1722 .concurrent_state()
1723 .pending
1724 .insert(call.thread, call.kind);
1725
1726 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1730 }
1731 }
1732 WorkItem::WorkerFunction(fun) => {
1733 self.run_on_worker(WorkerItem::Function(fun)).await?;
1734 }
1735 }
1736
1737 Ok(())
1738 })
1739 }
1740
1741 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1743 let state = self.0.concurrent_state_mut()?;
1744 if state.worker_item.is_some() {
1745 bail_bug!("worker item already set when taking worker fiber");
1746 }
1747 let worker = if let Some(fiber) = state.worker.take() {
1748 fiber
1749 } else {
1750 unsafe {
1769 fiber::make_fiber_unchecked(self.0, move |store| {
1770 loop {
1771 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1772 bail_bug!("worker_item not present when resuming fiber")
1773 };
1774 match item {
1775 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1776 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1777 }
1778
1779 store.suspend(SuspendReason::NeedWork)?;
1780 }
1781 })?
1782 }
1783 };
1784
1785 self.0
1788 .concurrent_state_mut_already_forced_current_thread()
1789 .worker_item = Some(item);
1790
1791 self.0.resume_fiber(worker).await
1792 }
1793
1794 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1799 where
1800 T: 'static,
1801 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1802 + Send
1803 + Sync
1804 + 'static,
1805 R: Send + Sync + 'static,
1806 {
1807 let token = StoreToken::new(self);
1808 async move {
1809 let mut accessor = Accessor::new(token);
1810 closure(&mut accessor).await
1811 }
1812 }
1813
1814 pub(crate) async fn start_instance(
1815 &mut self,
1816 instance: ModuleInstance,
1817 callee: Option<RuntimeInstance>,
1818 ) -> Result<ModuleInstance> {
1819 let (tx, rx) = oneshot::channel();
1820 let token = StoreToken::new(self.as_context_mut());
1821 self.0.queue_task(move |store| {
1822 _ = tx.send(
1823 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1824 .map(|()| instance),
1825 );
1826 Ok(())
1827 })?;
1828 self.as_context_mut()
1829 .run_concurrent_trap_on_idle(async |_| {
1830 rx.await
1831 .map_err(|_| format_err!("oneshot channel canceled"))
1832 })
1833 .await??
1834 }
1835}
1836
1837pub type EnteredHostTask = Option<QualifiedThreadId>;
1844
1845impl StoreOpaque {
1846 #[inline]
1850 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1851 if !self.concurrency_support() {
1853 return Ok(CurrentThread::None);
1854 }
1855
1856 if !self
1859 .vm_store_context_mut()
1860 .current_thread_mut()
1861 .is_deferred()
1862 {
1863 return Ok(self
1864 .concurrent_state_mut_already_forced_current_thread()
1865 .unforced_current_thread);
1866 }
1867
1868 self.force_deferred_current_thread()
1869 }
1870
1871 #[cold]
1874 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1875 let state = self.concurrent_state_mut_without_forcing_current_thread();
1884 let id = match state.unforced_current_thread.guest_task() {
1885 Some(task) => state.get_mut(task)?.instance.instance,
1886 None => bail_bug!("deferred component-model thread with non-guest base"),
1887 };
1888
1889 let mut frames = Vec::new();
1892 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1893 while let Some(ptr) = cur.as_deferred() {
1894 let deferred = unsafe { ptr.as_non_null().as_ref() };
1899 frames.push((
1900 deferred.callee_async != 0,
1901 deferred.callee_instance,
1902 deferred.saved_context,
1903 ));
1904 cur = deferred.parent;
1905 }
1906
1907 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1911
1912 let current_context = *self.vm_store_context_mut().component_context_mut();
1915
1916 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1920 *self.vm_store_context_mut().component_context_mut() = saved_context;
1924 let callee = RuntimeInstance {
1925 instance: id,
1926 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1927 };
1928 self.enter_guest_sync_call(callee_async, callee)?;
1929 }
1930
1931 *self.vm_store_context_mut().component_context_mut() = current_context;
1933
1934 Ok(self
1935 .concurrent_state_mut_without_forcing_current_thread()
1936 .unforced_current_thread)
1937 }
1938
1939 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1940 match self.current_thread()?.guest() {
1941 Some(id) => Ok(*id),
1942 None => bail_bug!("current thread is not a guest thread"),
1943 }
1944 }
1945
1946 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1950 match self.current_thread()? {
1951 CurrentThread::Host(id) => Ok(Some(id)),
1952 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1953 _ => bail_bug!("current thread is not a host thread"),
1954 }
1955 }
1956
1957 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1960 Ok(self
1961 .concurrent_state_mut()?
1962 .materialize_current_host_task_id()?)
1963 }
1964
1965 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1966 log::trace!("enter sync-typed call {callee:?}");
1967 let state = self.instance_state(callee).concurrent_state();
1968 let old_do_not_suspend = state.do_not_suspend;
1969 state.do_not_suspend = true;
1970
1971 let thread = self.current_guest_thread()?;
1972 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1973 if thread.old_do_not_suspend.is_some() {
1974 bail_bug!("current thread already has `old_do_not_suspend` value");
1975 }
1976
1977 thread.old_do_not_suspend = Some(old_do_not_suspend);
1978
1979 Ok(())
1980 }
1981
1982 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1983 log::trace!("exit sync-typed call {callee:?}");
1984 let thread = self.current_guest_thread()?;
1985 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1986 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1987 bail_bug!("current thread missing `old_do_not_suspend` value");
1988 };
1989 let state = self.instance_state(callee).concurrent_state();
1990 state.do_not_suspend = old_do_not_suspend;
1991 Ok(())
1992 }
1993
1994 pub(crate) fn enter_guest_sync_call(
2006 &mut self,
2007 callee_async_typed: bool,
2008 callee: RuntimeInstance,
2009 ) -> Result<()> {
2010 log::trace!("enter sync-lifted call {callee:?}");
2011 if !self.concurrency_support() {
2012 return self.enter_call_not_concurrent();
2013 }
2014
2015 let state = self.concurrent_state_mut()?;
2019 let item = state.next_switch_item.take();
2020 state.saved_next_switch_items.push(item);
2021
2022 let thread = self.current_thread()?;
2023 let caller = if let Some(thread) = thread.guest() {
2024 Caller::Guest { thread: *thread }
2025 } else {
2026 Caller::Host {
2027 tx: None,
2028 caller: self.materialize_host_task_id()?,
2029 }
2030 };
2031
2032 let state = self.concurrent_state_mut()?;
2033 let guest_thread = GuestTask::new(
2034 state,
2035 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2036 LiftResult {
2037 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2038 ty: TypeTupleIndex::reserved_value(),
2039 memory: None,
2040 string_encoding: StringEncoding::Utf8,
2041 },
2042 caller,
2043 None,
2044 callee,
2045 callee_async_typed,
2046 false,
2047 )?;
2048
2049 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2050 guest_thread.thread,
2051 self,
2052 callee.index,
2053 )?;
2054 self.set_thread(guest_thread)?;
2055
2056 if !callee_async_typed {
2057 self.enter_sync_call(callee)?;
2058 }
2059
2060 Ok(())
2061 }
2062
2063 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2071 if !self.concurrency_support() {
2072 return Ok(self.exit_call_not_concurrent());
2073 }
2074
2075 let thread = match self.current_thread()?.guest() {
2076 Some(t) => *t,
2077 None => bail_bug!("expected task when exiting"),
2078 };
2079 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2080 let instance = task.instance;
2081
2082 let caller = match &task.caller {
2083 &Caller::Guest { thread } => thread.into(),
2084 &Caller::Host { caller, .. } => caller
2085 .map(CurrentThread::Host)
2086 .unwrap_or(CurrentThread::None),
2087 };
2088 task.lift_result = None;
2089 task.exited = true;
2090 let async_typed = task.async_typed;
2091
2092 if !async_typed {
2093 self.exit_sync_call(instance)?;
2094 }
2095
2096 self.set_thread(caller)?;
2097
2098 log::trace!("exit sync-lifted call {instance:?}");
2099
2100 if async_typed {
2101 self.switch_or_trap_if_may_not_suspend(instance)?;
2106 }
2107
2108 self.cleanup_thread(thread, instance)?;
2109
2110 self.concurrent_state_mut()?
2111 .decrement_task_ref_count(thread.task)?;
2112
2113 let state = self.concurrent_state_mut()?;
2114 let Some(item) = state.saved_next_switch_items.pop() else {
2115 bail_bug!("unable to pop from `saved_next_switch_items`");
2116 };
2117 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2118 state.push_high_priority(item);
2121 bail_bug!("`next_switch_item` unexpectedly already set");
2122 }
2123
2124 Ok(())
2125 }
2126
2127 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2134 if !self.concurrency_support() {
2135 self.enter_call_not_concurrent()?;
2136 return Ok(None);
2137 }
2138 let caller = self.current_guest_thread()?;
2139 log::trace!("new deferred host task with caller {caller:?}");
2140
2141 self.set_thread(CurrentThread::DeferredHost(caller))?;
2142 let state = self.concurrent_state_mut()?;
2143 debug_assert!(state.deferred_host_call_context.is_none());
2144 state.deferred_host_call_context = Some(CallContext::default());
2145 state.debug_assert_deferred_host_invariant();
2146 Ok(Some(caller))
2147 }
2148
2149 pub(crate) fn host_task_delete(
2156 &mut self,
2157 original_task: EnteredHostTask,
2158 materialized_task: Option<TableId<HostTask>>,
2159 ) -> Result<()> {
2160 match original_task {
2161 Some(caller) => {
2162 self.set_thread(caller)?;
2163 if materialized_task.is_none() {
2164 let state = self.concurrent_state_mut()?;
2165 let context = state
2166 .deferred_host_call_context
2167 .take()
2168 .expect("deferred host call context should be present");
2169 debug_assert!(context.is_empty());
2170 state.debug_assert_deferred_host_invariant();
2171 }
2172 log::trace!(
2173 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2174 );
2175 if let Some(task) = materialized_task {
2176 self.concurrent_state_mut()?.delete_host_task(task)?;
2177 }
2178 }
2179 None => {
2180 debug_assert!(materialized_task.is_none());
2181 self.exit_call_not_concurrent();
2182 }
2183 }
2184 Ok(())
2185 }
2186
2187 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2190 self.component_instance_mut(instance.instance)
2191 .instance_state(instance.index)
2192 }
2193
2194 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2200 let thread = thread.into();
2201 let state = self.concurrent_state_mut()?;
2202 state.debug_assert_deferred_host_invariant();
2203 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2204
2205 state.handle_thread_switch(old_thread, thread)?;
2206
2207 if let Some(old_thread) = old_thread.guest() {
2215 let old_context = *self.vm_store_context_mut().component_context_mut();
2216 self.concurrent_state_mut()?
2217 .get_mut(old_thread.thread)?
2218 .context = old_context;
2219 }
2220 if cfg!(debug_assertions) {
2221 *self.vm_store_context_mut().component_context_mut() =
2222 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2223 }
2224 if let Some(thread) = thread.guest() {
2225 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2226 let context = thread.context;
2227 if cfg!(debug_assertions) {
2228 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2229 }
2230 *self.vm_store_context_mut().component_context_mut() = context;
2231 }
2232
2233 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2235 VMLazyThread::none()
2236 } else {
2237 VMLazyThread::forced()
2238 };
2239
2240 Ok(old_thread)
2241 }
2242
2243 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2245 if self.switch_if_may_not_suspend(instance)? {
2246 Ok(())
2247 } else {
2248 Err(Trap::CannotBlockSyncTask.into())
2249 }
2250 }
2251
2252 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2256 self.concurrent_state_mut()?;
2260
2261 Ok(!self.concurrency_support()
2262 || !self
2263 .instance_state(instance)
2264 .concurrent_state()
2265 .do_not_suspend
2266 || self
2267 .concurrent_state_mut()?
2268 .promote_instance_local_thread_work_item(instance)?)
2269 }
2270
2271 fn enter_instance(&mut self, instance: RuntimeInstance) {
2275 log::trace!("enter {instance:?}");
2276 self.instance_state(instance)
2277 .concurrent_state()
2278 .do_not_enter = true;
2279 }
2280
2281 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2285 log::trace!("exit {instance:?}");
2286 self.instance_state(instance)
2287 .concurrent_state()
2288 .do_not_enter = false;
2289 self.partition_pending(instance)
2290 }
2291
2292 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2300 for (thread, kind) in
2301 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2302 {
2303 let call = GuestCall { thread, kind };
2304 if call.is_ready(self)? {
2305 self.concurrent_state_mut()?
2306 .push_high_priority(WorkItem::GuestCall { instance, call });
2307 } else {
2308 self.instance_state(instance)
2309 .concurrent_state()
2310 .pending
2311 .insert(call.thread, call.kind);
2312 }
2313 }
2314
2315 if let Some(waker) = self
2316 .concurrent_state_mut()?
2317 .ready_for_concurrent_call_waker
2318 .take()
2319 {
2320 waker.wake();
2321 }
2322
2323 Ok(())
2324 }
2325
2326 pub(crate) fn backpressure_modify(
2328 &mut self,
2329 caller_instance: RuntimeInstance,
2330 modify: impl FnOnce(u16) -> Option<u16>,
2331 ) -> Result<()> {
2332 let state = self.instance_state(caller_instance).concurrent_state();
2333 let old = state.backpressure;
2334 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2335 state.backpressure = new;
2336
2337 if old > 0 && new == 0 {
2338 self.partition_pending(caller_instance)?;
2341 }
2342
2343 Ok(())
2344 }
2345
2346 fn resume_fiber(
2349 &mut self,
2350 fiber: StoreFiber<'static>,
2351 ) -> impl Future<Output = Result<()>> + '_ {
2352 let mut resuming = ResumingFiber {
2353 store: self,
2354 fiber: Some(fiber),
2355 };
2356 async move {
2357 let old_thread = resuming.store.current_thread()?;
2358 log::trace!("resume_fiber: save current thread {old_thread:?}");
2359
2360 let fiber = resuming.fiber.take().unwrap();
2361 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2362
2363 resuming.store.set_thread(old_thread)?;
2364
2365 let state = resuming.store.concurrent_state_mut()?;
2366
2367 if let Some(ot) = old_thread.guest() {
2368 state.set_thread_running(ot.thread)?;
2369 }
2370 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2371
2372 if resuming.fiber.is_some() {
2373 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2374 let reason = match state.suspend_reason.take() {
2376 Some(r) => r,
2377 None => bail_bug!("suspend reason missing when resuming fiber"),
2378 };
2379 match reason {
2380 SuspendReason::NeedWork => {
2381 if state.worker.is_none() {
2382 state.worker = resuming.fiber.take();
2383 }
2384 }
2385 SuspendReason::Yielding { thread } => {
2386 let slot = &mut state.get_mut(thread.thread)?.state;
2387 slot.check_no_fiber()?;
2388 *slot = GuestThreadState::Ready {
2389 fiber: resuming.fiber.take().unwrap(),
2390 };
2391 let instance = state.get_mut(thread.task)?.instance;
2392 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2393 }
2394 SuspendReason::ExplicitlySuspending { thread } => {
2395 let slot = &mut state.get_mut(thread.thread)?.state;
2396 slot.check_no_fiber()?;
2397 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2398 }
2399 SuspendReason::Waiting { set, thread } => {
2400 match state.get_mut(set)?.waiting.entry(thread) {
2401 Entry::Vacant(entry) => {
2402 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2403 }
2404 Entry::Occupied(_) => {
2405 bail_bug!("entry unexpectedly already exists for {thread:?}");
2406 }
2407 }
2408 }
2409 SuspendReason::YieldingToSubtask { thread } => {
2410 let instance = state.get_mut(thread.task)?.instance;
2419 if state.next_switch_item.is_some() {
2420 bail_bug!(
2423 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2424 a thread wanted to wait on a subtask"
2425 );
2426 }
2427 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2428 instance,
2429 thread,
2430 fiber: resuming.fiber.take().unwrap(),
2431 });
2432 log::trace!("set next switch item to {item:?}");
2433 }
2434 };
2435 } else {
2436 log::trace!("resume_fiber: fiber has exited");
2437 }
2438
2439 Ok(())
2440 }
2441 }
2442
2443 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2449 log::trace!("suspend fiber: {reason:?}");
2450
2451 let state = self.concurrent_state_mut()?;
2452
2453 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2460 SuspendReason::Yielding { .. }
2461 | SuspendReason::Waiting { .. }
2462 | SuspendReason::ExplicitlySuspending { .. } => {
2463 if state.switch_item.is_none() {
2466 state.take_next_switch_item()?;
2467 }
2468
2469 (true, false)
2470 }
2471 SuspendReason::YieldingToSubtask { .. } => (true, true),
2472 SuspendReason::NeedWork => (false, false),
2473 };
2474
2475 let old_next_switch_item = if save_and_restore_next_switch_item {
2476 Some(state.save_next_switch_item()?)
2477 } else {
2478 None
2479 };
2480
2481 let old_guest_thread = if save_and_restore_thread {
2482 self.current_thread()?
2483 } else {
2484 CurrentThread::None
2485 };
2486
2487 let waiting_set = match &reason {
2488 SuspendReason::Waiting { set, .. } => Some(*set),
2489 _ => None,
2490 };
2491
2492 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2493 assert!(suspend_reason.is_none());
2494 *suspend_reason = Some(reason);
2495
2496 if !self.fiber_async_state_mut().can_block() {
2499 return Err(format_err!("future dropped"));
2500 }
2501
2502 if let Some(set) = waiting_set {
2505 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2506 }
2507
2508 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2509
2510 if let Some(set) = waiting_set {
2511 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2512 }
2513
2514 if save_and_restore_thread {
2515 self.set_thread(old_guest_thread)?;
2516 }
2517
2518 if let Some(item) = old_next_switch_item {
2519 let state = self.concurrent_state_mut()?;
2520 state.restore_next_switch_item(item)?;
2521 }
2522
2523 Ok(())
2524 }
2525
2526 fn wait_for_event(
2527 &mut self,
2528 caller_instance: RuntimeInstance,
2529 waitable: Waitable,
2530 ) -> Result<()> {
2531 let caller = self.current_guest_thread()?;
2532 let state = self.concurrent_state_mut()?;
2533
2534 waitable.trap_if_in_waitable_set(state)?;
2535
2536 let set = state.get_mut(caller.thread)?.sync_call_set;
2537 waitable.join(state, Some(set))?;
2538
2539 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2540
2541 self.suspend(SuspendReason::Waiting {
2542 set,
2543 thread: caller,
2544 })?;
2545 let state = self.concurrent_state_mut()?;
2546
2547 waitable.join(state, None)
2548 }
2549
2550 fn cleanup_thread(
2572 &mut self,
2573 guest_thread: QualifiedThreadId,
2574 runtime_instance: RuntimeInstance,
2575 ) -> Result<()> {
2576 let state = self.concurrent_state_mut()?;
2577 state.take_next_switch_item()?;
2580 let thread_data = state.get_mut(guest_thread.thread)?;
2581 let sync_call_set = thread_data.sync_call_set;
2582 if let Some(guest_id) = thread_data.instance_rep {
2583 self.instance_state(runtime_instance)
2584 .thread_handle_table()
2585 .guest_thread_remove(guest_id)?;
2586 }
2587 let state = self.concurrent_state_mut()?;
2588
2589 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2591 if let Some(Event::Subtask {
2592 status: Status::Returned | Status::ReturnCancelled,
2593 }) = waitable.common(state)?.event
2594 {
2595 waitable.delete_from(state)?;
2596 }
2597 }
2598
2599 state.delete(guest_thread.thread)?;
2600 state.delete(sync_call_set)?;
2601 let task = state.get_mut(guest_thread.task)?;
2602 task.threads.remove(&guest_thread.thread);
2603
2604 if task.threads.is_empty() && !task.returned_or_cancelled() {
2605 bail!(Trap::NoAsyncResult);
2606 }
2607
2608 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2609 task.decremented_interesting_task_count = true;
2610
2611 debug_assert!(state.interesting_tasks > 0);
2612 state.interesting_tasks -= 1;
2613 if state.interesting_tasks == 0
2614 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2615 {
2616 waker.wake();
2617 }
2618 }
2619
2620 state.decrement_task_ref_count(guest_thread.task)?;
2621
2622 Ok(())
2623 }
2624
2625 fn cancel_guest_subtask_without_lowered_parameters(
2638 &mut self,
2639 caller_instance: RuntimeInstance,
2640 guest_task: TableId<GuestTask>,
2641 ) -> Result<()> {
2642 let concurrent_state = self.concurrent_state_mut()?;
2643 let task = concurrent_state.get_mut(guest_task)?;
2644 assert!(!task.already_lowered_parameters());
2645 task.lower_params = None;
2649 task.lift_result = None;
2650 task.exited = true;
2651 let instance = task.instance;
2652
2653 assert_eq!(1, task.threads.len());
2656 let thread = *task.threads.iter().next().unwrap();
2657 self.cleanup_thread(
2658 QualifiedThreadId {
2659 task: guest_task,
2660 thread,
2661 },
2662 caller_instance,
2663 )?;
2664
2665 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2667 let pending_count = pending.len();
2668 pending.retain(|thread, _| thread.task != guest_task);
2669 if pending.len() == pending_count {
2671 bail!(Trap::SubtaskCancelAfterTerminal);
2672 }
2673 Ok(())
2674 }
2675
2676 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2679 if !self.concurrency_support() {
2680 return Ok(self
2681 .current_scope_id_not_concurrent()?
2682 .map(|id| CurrentScope::Id(Scope::Id(id))));
2683 }
2684
2685 Ok(match self.current_thread()? {
2686 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2687 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2688 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2689 CurrentThread::None => return Ok(None),
2690 })
2691 }
2692
2693 pub(crate) fn queue_task(
2694 &mut self,
2695 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2696 ) -> Result<()> {
2697 self.concurrent_state_mut()?
2698 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2699 Ok(())
2700 }
2701
2702 fn any_may_not_suspend(&mut self) -> Result<bool> {
2711 Ok(self
2719 .concurrent_state_mut()?
2720 .table
2721 .get_mut()
2722 .iter_mut()
2723 .filter_map(|(_, entry)| {
2724 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2725 Some(task.instance)
2726 } else {
2727 None
2728 }
2729 })
2730 .collect::<Vec<_>>()
2731 .into_iter()
2732 .any(|instance| {
2733 self.instance_state(instance)
2734 .concurrent_state()
2735 .do_not_suspend
2736 }))
2737 }
2738}
2739
2740impl Instance {
2741 fn get_event(
2744 self,
2745 store: &mut StoreOpaque,
2746 guest_task: TableId<GuestTask>,
2747 set: Option<TableId<WaitableSet>>,
2748 cancellable: bool,
2749 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2750 let state = store.concurrent_state_mut()?;
2751
2752 let task = state.get_mut(guest_task)?;
2753 let event = &mut task.event;
2754 if let Some(ev) = event
2755 && (cancellable || !matches!(ev, Event::Cancelled))
2756 {
2757 log::trace!("deliver event {ev:?} to {guest_task:?}");
2758
2759 if matches!(ev, Event::Cancelled) {
2760 task.cancel_request_delivered = true;
2761 }
2762
2763 let ev = *ev;
2764 *event = None;
2765 return Ok(Some((ev, None)));
2766 }
2767
2768 let set = match set {
2769 Some(set) => set,
2770 None => return Ok(None),
2771 };
2772 let waitable = match state.get_mut(set)?.ready.pop_first() {
2773 Some(v) => v,
2774 None => return Ok(None),
2775 };
2776
2777 let common = waitable.common(state)?;
2778 let handle = match common.handle {
2779 Some(h) => h,
2780 None => bail_bug!("handle not set when delivering event"),
2781 };
2782 let event = match common.event.take() {
2783 Some(e) => e,
2784 None => bail_bug!("event not set when delivering event"),
2785 };
2786
2787 log::trace!(
2788 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2789 );
2790
2791 waitable.on_delivery(store, self, event)?;
2792
2793 Ok(Some((event, Some((waitable, handle)))))
2794 }
2795
2796 fn handle_callback_code(
2802 self,
2803 store: &mut StoreOpaque,
2804 guest_thread: QualifiedThreadId,
2805 runtime_instance: RuntimeComponentInstanceIndex,
2806 code: u32,
2807 ) -> Result<()> {
2808 let (code, set) = unpack_callback_code(code);
2809
2810 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2811
2812 let state = store.concurrent_state_mut()?;
2813
2814 state.take_next_switch_item()?;
2815
2816 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2817 let set = store
2818 .instance_state(self.runtime_instance(runtime_instance))
2819 .handle_table()
2820 .waitable_set_rep(handle)?;
2821
2822 Ok(TableId::<WaitableSet>::new(set))
2823 };
2824
2825 match code {
2826 callback_code::EXIT => {
2827 log::trace!("implicit thread {guest_thread:?} completed");
2828 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2829 task.exited = true;
2830 task.callback = None;
2831
2832 let runtime_instance = self.runtime_instance(runtime_instance);
2833
2834 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2839
2840 store.cleanup_thread(guest_thread, runtime_instance)?;
2841 }
2842 callback_code::YIELD => {
2843 let old = state
2846 .get_mut(guest_thread.thread)?
2847 .wake_on_cancel
2848 .replace(WakeOnCancel::Yielding);
2849 if !old.is_none() {
2850 bail_bug!("thread unexpectedly had wake_on_cancel set");
2851 }
2852
2853 let call = GuestCall {
2860 thread: guest_thread,
2861 kind: GuestCallKind::DeliverEvent {
2862 instance: self,
2863 set: None,
2864 },
2865 };
2866 state.push_low_priority(WorkItem::GuestCall {
2869 instance: self.runtime_instance(runtime_instance),
2870 call,
2871 });
2872 }
2873 callback_code::WAIT => {
2874 let set = get_set(store, set)?;
2875 self.wait_with_callback(store, guest_thread, set)?;
2876 }
2877 _ => bail!(Trap::UnsupportedCallbackCode),
2878 }
2879
2880 Ok(())
2881 }
2882
2883 fn wait_with_callback(
2889 self,
2890 store: &mut StoreOpaque,
2891 guest_thread: QualifiedThreadId,
2892 set: TableId<WaitableSet>,
2893 ) -> Result<()> {
2894 let state = store.concurrent_state_mut()?;
2895 state.get_mut(set)?.num_waiting += 1;
2898
2899 if state.get_mut(guest_thread.task)?.event.is_some()
2900 || !state.get_mut(set)?.ready.is_empty()
2901 {
2902 let instance = state.get_mut(guest_thread.task)?.instance;
2904 state.push_high_priority(WorkItem::GuestCall {
2905 instance,
2906 call: GuestCall {
2907 thread: guest_thread,
2908 kind: GuestCallKind::DeliverEvent {
2909 instance: self,
2910 set: Some(set),
2911 },
2912 },
2913 });
2914 return Ok(());
2915 }
2916
2917 let instance = store
2923 .concurrent_state_mut()?
2924 .get_mut(guest_thread.task)?
2925 .instance;
2926 store.switch_or_trap_if_may_not_suspend(instance)?;
2927
2928 let state = store.concurrent_state_mut()?;
2934 let old = state
2935 .get_mut(guest_thread.thread)?
2936 .wake_on_cancel
2937 .replace(WakeOnCancel::Waiting(set));
2938 if !old.is_none() {
2939 bail_bug!("thread unexpectedly had wake_on_cancel set");
2940 }
2941 let old = state
2942 .get_mut(set)?
2943 .waiting
2944 .insert(guest_thread, WaitMode::Callback(self));
2945 if !old.is_none() {
2946 bail_bug!("set's waiting set already had this thread registered");
2947 }
2948 Ok(())
2949 }
2950
2951 unsafe fn stage_call<T: 'static>(
2958 self,
2959 mut store: StoreContextMut<T>,
2960 guest_thread: QualifiedThreadId,
2961 callee: SendSyncPtr<VMFuncRef>,
2962 param_count: usize,
2963 result_count: usize,
2964 async_: bool,
2965 callback: Option<SendSyncPtr<VMFuncRef>>,
2966 post_return: Option<SendSyncPtr<VMFuncRef>>,
2967 host_caller: bool,
2968 ) -> Result<()> {
2969 unsafe fn make_call<T: 'static>(
2984 store: StoreContextMut<T>,
2985 guest_thread: QualifiedThreadId,
2986 callee: SendSyncPtr<VMFuncRef>,
2987 param_count: usize,
2988 result_count: usize,
2989 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2990 + Send
2991 + Sync
2992 + 'static
2993 + use<T> {
2994 let token = StoreToken::new(store);
2995 move |store: &mut dyn VMStore| {
2996 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2997
2998 store
2999 .concurrent_state_mut()?
3000 .set_thread_running(guest_thread.thread)?;
3001 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3002 let lower = match task.lower_params.take() {
3003 Some(l) => l,
3004 None => bail_bug!("lower_params missing"),
3005 };
3006
3007 lower(store, &mut storage[..param_count])?;
3008
3009 let mut store = token.as_context_mut(store);
3010
3011 unsafe {
3014 crate::Func::call_unchecked_raw(
3015 &mut store,
3016 callee.as_non_null(),
3017 NonNull::new(
3018 &mut storage[..param_count.max(result_count)]
3019 as *mut [MaybeUninit<ValRaw>] as _,
3020 )
3021 .unwrap(),
3022 UncaughtException::Trap,
3023 )?;
3024 }
3025
3026 Ok(storage)
3027 }
3028 }
3029
3030 let call = unsafe {
3034 make_call(
3035 store.as_context_mut(),
3036 guest_thread,
3037 callee,
3038 param_count,
3039 result_count,
3040 )
3041 };
3042
3043 let callee_instance = store
3044 .0
3045 .concurrent_state_mut()?
3046 .get_mut(guest_thread.task)?
3047 .instance;
3048
3049 let fun = if callback.is_some() {
3050 assert!(async_);
3051
3052 Box::new(move |store: &mut dyn VMStore| {
3053 self.add_guest_thread_to_instance_table(
3054 guest_thread.thread,
3055 store,
3056 callee_instance.index,
3057 )?;
3058 let old_thread = store.set_thread(guest_thread)?;
3059 log::trace!(
3060 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
3061 );
3062
3063 store.enter_instance(callee_instance);
3064
3065 let storage = call(store)?;
3072
3073 store.exit_instance(callee_instance)?;
3074
3075 store.set_thread(old_thread)?;
3076 let state = store.concurrent_state_mut()?;
3077 if let Some(t) = old_thread.guest() {
3078 state.set_thread_running(t.thread)?;
3079 }
3080 log::trace!("stackless call: restored {old_thread:?} as current thread");
3081
3082 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
3085
3086 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
3087 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3088 } else {
3089 let token = StoreToken::new(store.as_context_mut());
3090 Box::new(move |store: &mut dyn VMStore| {
3091 self.add_guest_thread_to_instance_table(
3092 guest_thread.thread,
3093 store,
3094 callee_instance.index,
3095 )?;
3096 let old_thread = store.set_thread(guest_thread)?;
3097 log::trace!(
3098 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3099 );
3100 let flags = self.id().get(store).instance_flags(callee_instance.index);
3101
3102 let callee_async_typed = store
3103 .concurrent_state_mut()?
3104 .get_mut(guest_thread.task)?
3105 .async_typed;
3106
3107 if !async_ && callee_async_typed {
3111 store.enter_instance(callee_instance);
3112 }
3113
3114 if !callee_async_typed {
3115 store.enter_sync_call(callee_instance)?;
3116 }
3117
3118 let storage = call(store)?;
3125
3126 if !callee_async_typed {
3127 store.exit_sync_call(callee_instance)?;
3128 }
3129
3130 if !async_ {
3131 if callee_async_typed {
3137 store.exit_instance(callee_instance)?;
3138 }
3139
3140 let lift = {
3141 let state = store.concurrent_state_mut()?;
3142 if !state.get_mut(guest_thread.task)?.result.is_none() {
3143 bail_bug!("task has already produced a result");
3144 }
3145
3146 match state.get_mut(guest_thread.task)?.lift_result.take() {
3147 Some(lift) => lift,
3148 None => bail_bug!("lift_result field is missing"),
3149 }
3150 };
3151
3152 let result = (lift.lift)(store, unsafe {
3155 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3156 &storage[..result_count],
3157 )
3158 })?;
3159
3160 let post_return_arg = match result_count {
3161 0 => ValRaw::i32(0),
3162 1 => unsafe { storage[0].assume_init() },
3165 _ => unreachable!(),
3166 };
3167
3168 unsafe {
3169 call_post_return(
3170 token.as_context_mut(store),
3171 post_return.map(|v| v.as_non_null()),
3172 post_return_arg,
3173 flags,
3174 )?;
3175 }
3176
3177 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3178 }
3179
3180 store.set_thread(old_thread)?;
3181
3182 store
3183 .concurrent_state_mut()?
3184 .get_mut(guest_thread.task)?
3185 .exited = true;
3186
3187 log::trace!(
3188 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3189 );
3190
3191 if callee_async_typed {
3192 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3197 }
3198
3199 store.cleanup_thread(guest_thread, callee_instance)?;
3201 Ok(())
3202 })
3203 };
3204
3205 store.0.concurrent_state_mut()?.push_work_item(
3206 WorkItem::GuestCall {
3207 instance: callee_instance,
3208 call: GuestCall {
3209 thread: guest_thread,
3210 kind: GuestCallKind::StartImplicit(fun),
3211 },
3212 },
3213 if host_caller {
3214 Priority::High
3215 } else {
3216 Priority::Switch
3217 },
3218 )?;
3219
3220 Ok(())
3221 }
3222
3223 unsafe fn prepare_call<T: 'static>(
3236 self,
3237 mut store: StoreContextMut<T>,
3238 start: NonNull<VMFuncRef>,
3239 return_: NonNull<VMFuncRef>,
3240 caller_instance: RuntimeComponentInstanceIndex,
3241 callee_instance: RuntimeComponentInstanceIndex,
3242 task_return_type: TypeTupleIndex,
3243 callee_async_typed: bool,
3244 memory: *mut VMMemoryDefinition,
3245 string_encoding: StringEncoding,
3246 caller_info: CallerInfo,
3247 ) -> Result<()> {
3248 enum ResultInfo {
3249 Heap { results: u32 },
3250 Stack { result_count: u32 },
3251 }
3252
3253 let result_info = match &caller_info {
3254 CallerInfo::Async {
3255 has_result: true,
3256 params,
3257 } => ResultInfo::Heap {
3258 results: match params.last() {
3259 Some(r) => r.get_u32(),
3260 None => bail_bug!("retptr missing"),
3261 },
3262 },
3263 CallerInfo::Async {
3264 has_result: false, ..
3265 } => ResultInfo::Stack { result_count: 0 },
3266 CallerInfo::Sync {
3267 result_count,
3268 params,
3269 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3270 results: match params.last() {
3271 Some(r) => r.get_u32(),
3272 None => bail_bug!("arg ptr missing"),
3273 },
3274 },
3275 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3276 result_count: *result_count,
3277 },
3278 };
3279
3280 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3281
3282 let start = SendSyncPtr::new(start);
3286 let return_ = SendSyncPtr::new(return_);
3287 let token = StoreToken::new(store.as_context_mut());
3288 let old_thread = store.0.current_guest_thread()?;
3289
3290 let state = store.0.concurrent_state_mut()?;
3291
3292 debug_assert_eq!(
3293 state.get_mut(old_thread.task)?.instance,
3294 self.runtime_instance(caller_instance)
3295 );
3296
3297 let guest_thread = GuestTask::new(
3298 state,
3299 Box::new(move |store, dst| {
3300 let mut store = token.as_context_mut(store);
3301 assert!(dst.len() <= MAX_FLAT_PARAMS);
3302 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3304 let count = match caller_info {
3305 CallerInfo::Async { params, has_result } => {
3309 let params = ¶ms[..params.len() - usize::from(has_result)];
3310 for (param, src) in params.iter().zip(&mut src) {
3311 src.write(*param);
3312 }
3313 params.len()
3314 }
3315
3316 CallerInfo::Sync { params, .. } => {
3318 for (param, src) in params.iter().zip(&mut src) {
3319 src.write(*param);
3320 }
3321 params.len()
3322 }
3323 };
3324 unsafe {
3331 crate::Func::call_unchecked_raw(
3332 &mut store,
3333 start.as_non_null(),
3334 NonNull::new(
3335 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3336 )
3337 .unwrap(),
3338 UncaughtException::Trap,
3339 )?;
3340 }
3341 dst.copy_from_slice(&src[..dst.len()]);
3342 let task = store.0.current_guest_thread()?.task;
3343 let state = store.0.concurrent_state_mut()?;
3344 Waitable::Guest(task).set_event(
3345 state,
3346 Some(Event::Subtask {
3347 status: Status::Started,
3348 }),
3349 )?;
3350 Ok(())
3351 }),
3352 LiftResult {
3353 lift: Box::new(move |store, src| {
3354 let mut store = token.as_context_mut(store);
3357 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3359 my_src.push(ValRaw::u32(*results));
3360 }
3361
3362 unsafe {
3369 crate::Func::call_unchecked_raw(
3370 &mut store,
3371 return_.as_non_null(),
3372 my_src.as_mut_slice().into(),
3373 UncaughtException::Trap,
3374 )?;
3375 }
3376
3377 let thread = store.0.current_guest_thread()?;
3378 let state = store.0.concurrent_state_mut()?;
3379 if sync_caller {
3380 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3381 if let ResultInfo::Stack { result_count } = &result_info {
3382 match result_count {
3383 0 => None,
3384 1 => Some(my_src[0]),
3385 _ => unreachable!(),
3386 }
3387 } else {
3388 None
3389 },
3390 );
3391 }
3392 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3393 }),
3394 ty: task_return_type,
3395 memory: NonNull::new(memory).map(SendSyncPtr::new),
3396 string_encoding,
3397 },
3398 Caller::Guest { thread: old_thread },
3399 None,
3400 self.runtime_instance(callee_instance),
3401 callee_async_typed,
3402 false,
3405 )?;
3406
3407 store.0.set_thread(guest_thread)?;
3410 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3411
3412 Ok(())
3413 }
3414
3415 unsafe fn call_callback<T>(
3420 self,
3421 mut store: StoreContextMut<T>,
3422 function: SendSyncPtr<VMFuncRef>,
3423 event: Event,
3424 handle: u32,
3425 ) -> Result<u32> {
3426 let (ordinal, result) = event.parts();
3427 let params = &mut [
3428 ValRaw::u32(ordinal),
3429 ValRaw::u32(handle),
3430 ValRaw::u32(result),
3431 ];
3432 unsafe {
3437 crate::Func::call_unchecked_raw(
3438 &mut store,
3439 function.as_non_null(),
3440 params.as_mut_slice().into(),
3441 UncaughtException::Trap,
3442 )?;
3443 }
3444 Ok(params[0].get_u32())
3445 }
3446
3447 unsafe fn start_call<T: 'static>(
3465 self,
3466 mut store: StoreContextMut<T>,
3467 callback: *mut VMFuncRef,
3468 post_return: *mut VMFuncRef,
3469 callee: NonNull<VMFuncRef>,
3470 param_count: u32,
3471 result_count: u32,
3472 flags: u32,
3473 storage: &mut [MaybeUninit<ValRaw>],
3474 ) -> Result<()> {
3475 let token = StoreToken::new(store.as_context_mut());
3476 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3477 let guest_thread = store.0.current_guest_thread()?;
3478 let state = store.0.concurrent_state_mut()?;
3479
3480 if !state.event_loop_running {
3481 bail_bug!("Instance::start_call called without a running event loop");
3482 }
3483
3484 let callee = SendSyncPtr::new(callee);
3485 let param_count = usize::try_from(param_count)?;
3486 assert!(param_count <= MAX_FLAT_PARAMS);
3487 let result_count = usize::try_from(result_count)?;
3488 assert!(result_count <= MAX_FLAT_RESULTS);
3489
3490 let task = state.get_mut(guest_thread.task)?;
3491 let callee_async_typed = task.async_typed;
3492 let callee_instance = task.instance;
3493
3494 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3495
3496 if let Some(callback) = NonNull::new(callback) {
3497 let callback = SendSyncPtr::new(callback);
3501 task.callback = Some(Box::new(move |store, event, handle| {
3502 let store = token.as_context_mut(store);
3503 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3504 }));
3505 }
3506
3507 let Caller::Guest { thread: caller } = &task.caller else {
3508 bail_bug!("start_call unexpectedly invoked for host->guest call");
3511 };
3512 let caller = *caller;
3513 let caller_instance = state.get_mut(caller.task)?.instance;
3514
3515 unsafe {
3517 self.stage_call(
3518 store.as_context_mut(),
3519 guest_thread,
3520 callee,
3521 param_count,
3522 result_count,
3523 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3524 NonNull::new(callback).map(SendSyncPtr::new),
3525 NonNull::new(post_return).map(SendSyncPtr::new),
3526 false,
3527 )?;
3528 }
3529
3530 let old_do_not_suspend = if callee_async_typed {
3531 let state = store.0.instance_state(callee_instance).concurrent_state();
3538 let old_do_not_suspend = state.do_not_suspend;
3539 state.do_not_suspend = false;
3540 Some(old_do_not_suspend)
3541 } else {
3542 None
3543 };
3544
3545 let state = store.0.concurrent_state_mut()?;
3546
3547 let guest_waitable = Waitable::Guest(guest_thread.task);
3550 let old_set = guest_waitable.common(state)?.set;
3551 let set = state.get_mut(caller.thread)?.sync_call_set;
3552 guest_waitable.join(state, Some(set))?;
3553
3554 store.0.set_thread(CurrentThread::None)?;
3555
3556 let mut yielded = false;
3572 let (status, waitable) = loop {
3573 store.0.suspend(if yielded {
3574 SuspendReason::Waiting {
3575 set,
3576 thread: caller,
3577 }
3578 } else {
3579 yielded = true;
3580 SuspendReason::YieldingToSubtask { thread: caller }
3581 })?;
3582
3583 if let Some(old_do_not_suspend) = old_do_not_suspend {
3584 store
3585 .0
3586 .instance_state(callee_instance)
3587 .concurrent_state()
3588 .do_not_suspend = old_do_not_suspend;
3589 }
3590
3591 let state = store.0.concurrent_state_mut()?;
3592
3593 log::trace!("taking event for {:?}", guest_thread.task);
3594 let event = guest_waitable.take_event(state)?;
3595 let Some(Event::Subtask { status }) = event else {
3596 bail_bug!("subtasks should only get subtask events, got {event:?}")
3597 };
3598
3599 log::trace!("status {status:?} for {:?}", guest_thread.task);
3600
3601 if status == Status::Returned {
3602 break (status, None);
3604 } else if async_caller {
3605 let handle = store
3609 .0
3610 .instance_state(caller_instance)
3611 .handle_table()
3612 .subtask_insert_guest(guest_thread.task.rep())?;
3613 store
3614 .0
3615 .concurrent_state_mut()?
3616 .get_mut(guest_thread.task)?
3617 .common
3618 .handle = Some(handle);
3619 break (status, Some(handle));
3620 } else {
3621 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3625 }
3626 };
3627
3628 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3629
3630 store.0.set_thread(caller)?;
3632
3633 store
3634 .0
3635 .concurrent_state_mut()?
3636 .set_thread_running(caller.thread)?;
3637
3638 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3639
3640 if async_caller {
3641 let Some(slot) = storage.first_mut() else {
3642 bail_bug!("no storage for async call status");
3643 };
3644 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3645 } else {
3646 let state = store.0.concurrent_state_mut()?;
3649 let task = state.get_mut(guest_thread.task)?;
3650 if let Some(Some(result)) = task.sync_result.take()? {
3651 storage[0] = MaybeUninit::new(result);
3652 }
3653 }
3654
3655 if status == Status::Returned {
3656 store
3657 .0
3658 .concurrent_state_mut()?
3659 .decrement_task_ref_count(guest_thread.task)?;
3660 }
3661
3662 Ok(())
3663 }
3664
3665 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3681 self,
3682 mut store: StoreContextMut<'_, T>,
3683 host_task: EnteredHostTask,
3684 result_may_require_realloc: bool,
3685 future: impl Future<Output = Result<R>> + Send + 'static,
3686 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3687 + Send
3688 + 'static,
3689 ) -> Result<u32> {
3690 let token = StoreToken::new(store.as_context_mut());
3691
3692 let (join_handle, future) = JoinHandle::run(future);
3695 let mut future = Box::pin(future);
3696
3697 let poll = tls::set(store.0, || {
3702 future
3703 .as_mut()
3704 .poll(&mut Context::from_waker(&Waker::noop()))
3705 });
3706
3707 match poll {
3708 Poll::Ready(result) => {
3710 let result = result.transpose()?;
3711 let task = store.0.current_materialized_host_task()?;
3714 lower(store.as_context_mut(), result, true, task)?;
3715 return Ok(Status::Returned.pack(None));
3716 }
3717
3718 Poll::Pending => {}
3720 }
3721
3722 let Some(task) = store.0.materialize_host_task_id()? else {
3726 bail_bug!("current thread is not a host thread")
3727 };
3728 {
3729 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3730 assert!(matches!(state, HostTaskState::CalleeStarted));
3731 *state = HostTaskState::CalleeRunning(join_handle);
3732 }
3733
3734 let future = Box::pin(async move {
3742 let result = match run_with_host_task_set(task, future).await? {
3743 Some(result) => Some(result?),
3744 None => None,
3745 };
3746 let on_complete = move |store: &mut dyn VMStore| {
3747 let mut store = token.as_context_mut(store);
3751 let old = store.0.set_thread(task)?;
3752
3753 let status = if result.is_some() {
3754 Status::Returned
3755 } else {
3756 Status::ReturnCancelled
3757 };
3758
3759 lower(store.as_context_mut(), result, false, Some(task))?;
3760 let state = store.0.concurrent_state_mut()?;
3761 match &mut state.get_mut(task)?.state {
3762 pending @ HostTaskState::CalleeCancelling => {
3765 *pending = HostTaskState::CalleeDone { cancelled: true };
3766 }
3767
3768 other => *other = HostTaskState::CalleeDone { cancelled: false },
3770 }
3771 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3772
3773 store.0.set_thread(old)?;
3774 Ok(())
3775 };
3776
3777 tls::get(move |store| {
3778 if result_may_require_realloc {
3779 store
3784 .concurrent_state_mut()?
3785 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3786 on_complete,
3787 ))));
3788 Ok(())
3789 } else {
3790 on_complete(store)
3793 }
3794 })
3795 });
3796
3797 let caller = match host_task {
3800 Some(caller) => caller,
3801 None => bail_bug!("host task wasn't created but should have been"),
3802 };
3803 let state = store.0.concurrent_state_mut()?;
3804 state.push_future(future);
3805 let instance = state.get_mut(caller.task)?.instance;
3806 let handle = store
3807 .0
3808 .instance_state(instance)
3809 .handle_table()
3810 .subtask_insert_host(task.rep())?;
3811 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3812 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3813
3814 store.0.set_thread(caller)?;
3818 Ok(Status::Started.pack(Some(handle)))
3819 }
3820
3821 pub(crate) fn task_return(
3824 self,
3825 store: &mut dyn VMStore,
3826 ty: TypeTupleIndex,
3827 options: OptionsIndex,
3828 storage: &[ValRaw],
3829 ) -> Result<()> {
3830 let guest_thread = store.current_guest_thread()?;
3831 let state = store.concurrent_state_mut()?;
3832 if !state.get_mut(guest_thread.task)?.async_lifted {
3833 bail!(Trap::TaskReturnOrCancelSyncLifted);
3834 }
3835 let lift = state
3836 .get_mut(guest_thread.task)?
3837 .lift_result
3838 .take()
3839 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3840 if !state.get_mut(guest_thread.task)?.result.is_none() {
3841 bail_bug!("task result unexpectedly already set");
3842 }
3843
3844 let CanonicalOptions {
3845 string_encoding,
3846 data_model,
3847 ..
3848 } = &self.id().get(store).component().env_component().options[options];
3849
3850 let invalid = ty != lift.ty
3851 || string_encoding != &lift.string_encoding
3852 || match data_model {
3853 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3854 Some(memory) => {
3855 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3856 let actual = self.id().get(store).runtime_memory(memory);
3857 expected != actual.as_ptr()
3858 }
3859 None => false,
3862 },
3863 CanonicalOptionsDataModel::Gc { .. } => true,
3865 };
3866
3867 if invalid {
3868 bail!(Trap::TaskReturnInvalid);
3869 }
3870
3871 log::trace!("task.return for {guest_thread:?}");
3872
3873 let result = (lift.lift)(store, storage)?;
3874 self.task_complete(store, guest_thread.task, result, Status::Returned)
3875 }
3876
3877 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3879 let guest_thread = store.current_guest_thread()?;
3880 let state = store.concurrent_state_mut()?;
3881 let task = state.get_mut(guest_thread.task)?;
3882 if !task.async_lifted {
3883 bail!(Trap::TaskReturnOrCancelSyncLifted);
3884 }
3885 if !task.cancel_request_delivered {
3886 bail!(Trap::TaskCancelNotCancelled);
3887 }
3888 _ = task
3889 .lift_result
3890 .take()
3891 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3892
3893 if !task.result.is_none() {
3894 bail_bug!("task result should not bet set yet");
3895 }
3896
3897 log::trace!("task.cancel for {guest_thread:?}");
3898
3899 self.task_complete(
3900 store,
3901 guest_thread.task,
3902 Box::new(DummyResult),
3903 Status::ReturnCancelled,
3904 )
3905 }
3906
3907 fn task_complete(
3913 self,
3914 store: &mut StoreOpaque,
3915 guest_task: TableId<GuestTask>,
3916 result: Box<dyn Any + Send + Sync>,
3917 status: Status,
3918 ) -> Result<()> {
3919 store
3920 .component_resource_tables(Some(self))?
3921 .validate_scope_exit()?;
3922
3923 let state = store.concurrent_state_mut()?;
3924 let task = state.get_mut(guest_task)?;
3925
3926 task.event = None;
3930
3931 if let Caller::Host { tx, .. } = &mut task.caller {
3932 if let Some(tx) = tx.take() {
3933 _ = tx.send(result);
3934 }
3935 } else {
3936 task.result = Some(result);
3937 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3938 }
3939
3940 Ok(())
3941 }
3942
3943 pub(crate) fn waitable_set_new(
3945 self,
3946 store: &mut StoreOpaque,
3947 caller_instance: RuntimeComponentInstanceIndex,
3948 ) -> Result<u32> {
3949 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3950 let handle = store
3951 .instance_state(self.runtime_instance(caller_instance))
3952 .handle_table()
3953 .waitable_set_insert(set.rep())?;
3954 log::trace!("new waitable set {set:?} (handle {handle})");
3955 Ok(handle)
3956 }
3957
3958 pub(crate) fn waitable_set_drop(
3960 self,
3961 store: &mut StoreOpaque,
3962 caller_instance: RuntimeComponentInstanceIndex,
3963 set: u32,
3964 ) -> Result<()> {
3965 let rep = store
3966 .instance_state(self.runtime_instance(caller_instance))
3967 .handle_table()
3968 .waitable_set_remove(set)?;
3969
3970 log::trace!("drop waitable set {rep} (handle {set})");
3971
3972 let set = store
3976 .concurrent_state_mut()?
3977 .get_mut(TableId::<WaitableSet>::new(rep))?;
3978 if set.num_waiting > 0 {
3979 bail!(Trap::WaitableSetDropHasWaiters);
3980 }
3981
3982 store
3983 .concurrent_state_mut()?
3984 .delete(TableId::<WaitableSet>::new(rep))?;
3985
3986 Ok(())
3987 }
3988
3989 pub(crate) fn waitable_join(
3991 self,
3992 store: &mut StoreOpaque,
3993 caller_instance: RuntimeComponentInstanceIndex,
3994 waitable_handle: u32,
3995 set_handle: u32,
3996 ) -> Result<()> {
3997 let mut instance = self.id().get_mut(store);
3998 let waitable =
3999 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4000
4001 let set = if set_handle == 0 {
4002 None
4003 } else {
4004 let set = instance.instance_states().0[caller_instance]
4005 .handle_table()
4006 .waitable_set_rep(set_handle)?;
4007
4008 Some(TableId::<WaitableSet>::new(set))
4009 };
4010
4011 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4012
4013 log::trace!(
4014 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4015 );
4016
4017 waitable.join(store.concurrent_state_mut()?, set)
4018 }
4019
4020 pub(crate) fn subtask_drop(
4022 self,
4023 store: &mut StoreOpaque,
4024 caller_instance: RuntimeComponentInstanceIndex,
4025 task_id: u32,
4026 ) -> Result<()> {
4027 self.waitable_join(store, caller_instance, task_id, 0)?;
4028
4029 let (rep, is_host) = store
4030 .instance_state(self.runtime_instance(caller_instance))
4031 .handle_table()
4032 .subtask_remove(task_id)?;
4033
4034 let concurrent_state = store.concurrent_state_mut()?;
4035 let waitable = if is_host {
4036 let id = TableId::<HostTask>::new(rep);
4037 let task = concurrent_state.get_mut(id)?;
4038 match &task.state {
4039 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4040 bail!(Trap::SubtaskDropNotResolved)
4041 }
4042 HostTaskState::CalleeDone { .. } => {}
4043 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4044 bail_bug!("invalid state for callee in `subtask.drop`")
4045 }
4046 }
4047
4048 Waitable::Host(id)
4049 } else {
4050 let id = TableId::<GuestTask>::new(rep);
4051 let task = concurrent_state.get_mut(id)?;
4052 if task.lift_result.is_some() {
4053 bail!(Trap::SubtaskDropNotResolved);
4054 }
4055 Waitable::Guest(id)
4056 };
4057
4058 waitable.common(concurrent_state)?.handle = None;
4059
4060 if waitable.take_event(concurrent_state)?.is_some() {
4063 bail!(Trap::SubtaskDropNotResolved);
4064 }
4065
4066 waitable.delete_from(concurrent_state)?;
4067
4068 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4069 Ok(())
4070 }
4071
4072 pub(crate) fn waitable_set_wait(
4074 self,
4075 store: &mut StoreOpaque,
4076 options: OptionsIndex,
4077 set: u32,
4078 payload: u32,
4079 ) -> Result<u32> {
4080 let &CanonicalOptions {
4081 instance: caller_instance,
4082 ..
4083 } = &self.id().get(store).component().env_component().options[options];
4084 let caller = self.runtime_instance(caller_instance);
4085 let rep = store
4086 .instance_state(self.runtime_instance(caller_instance))
4087 .handle_table()
4088 .waitable_set_rep(set)?;
4089
4090 self.waitable_check(
4091 store,
4092 caller,
4093 WaitableCheck::Wait,
4094 WaitableCheckParams {
4095 set: TableId::new(rep),
4096 options,
4097 payload,
4098 },
4099 )
4100 }
4101
4102 pub(crate) fn waitable_set_poll(
4104 self,
4105 store: &mut StoreOpaque,
4106 options: OptionsIndex,
4107 set: u32,
4108 payload: u32,
4109 ) -> Result<u32> {
4110 let &CanonicalOptions {
4111 instance: caller_instance,
4112 ..
4113 } = &self.id().get(store).component().env_component().options[options];
4114 let caller = self.runtime_instance(caller_instance);
4115 let rep = store
4116 .instance_state(caller)
4117 .handle_table()
4118 .waitable_set_rep(set)?;
4119
4120 self.waitable_check(
4121 store,
4122 caller,
4123 WaitableCheck::Poll,
4124 WaitableCheckParams {
4125 set: TableId::new(rep),
4126 options,
4127 payload,
4128 },
4129 )
4130 }
4131
4132 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4134 let thread_id = store.current_guest_thread()?.thread;
4135 match store
4136 .concurrent_state_mut()?
4137 .get_mut(thread_id)?
4138 .instance_rep
4139 {
4140 Some(r) => Ok(r),
4141 None => bail_bug!("thread should have instance_rep by now"),
4142 }
4143 }
4144
4145 pub(crate) fn thread_new_indirect<T: 'static>(
4147 self,
4148 mut store: StoreContextMut<T>,
4149 runtime_instance: RuntimeComponentInstanceIndex,
4150 start_func_ty: ModuleInternedTypeIndex,
4151 start_func_table_idx: RuntimeTableIndex,
4152 start_func_idx: u32,
4153 context: i32,
4154 ) -> Result<u32> {
4155 log::trace!("creating new thread");
4156
4157 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4158 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4159 else {
4160 bail_bug!("thread.new-indirect start function type should be registered");
4161 };
4162 let callee = instance
4163 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4164 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4165 if callee.type_index(store.0) != start_func_ty {
4166 bail!(Trap::ThreadNewIndirectInvalidType);
4167 }
4168
4169 let token = StoreToken::new(store.as_context_mut());
4170 let start_func = Box::new(
4171 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4172 let old_thread = store.set_thread(guest_thread)?;
4173 log::trace!(
4174 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4175 );
4176
4177 let mut store = token.as_context_mut(store);
4178 let mut params = [ValRaw::i32(context)];
4179 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4182
4183 store.0.set_thread(old_thread)?;
4184
4185 let runtime_instance = self.runtime_instance(runtime_instance);
4186
4187 store
4190 .0
4191 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4192
4193 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4194
4195 log::trace!("explicit thread {guest_thread:?} completed");
4196 let state = store.0.concurrent_state_mut()?;
4197 if let Some(t) = old_thread.guest() {
4198 state.set_thread_running(t.thread)?;
4199 }
4200 log::trace!("thread start: restored {old_thread:?} as current thread");
4201
4202 Ok(())
4203 },
4204 );
4205
4206 let current_thread = store.0.current_guest_thread()?;
4207 let state = store.0.concurrent_state_mut()?;
4208 let parent_task = current_thread.task;
4209
4210 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4211 let thread_id = state.push(new_thread)?;
4212 state.get_mut(parent_task)?.threads.insert(thread_id);
4213
4214 log::trace!("new thread with id {thread_id:?} created");
4215
4216 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4217 }
4218
4219 pub(crate) fn resume_thread(
4220 self,
4221 store: &mut StoreOpaque,
4222 runtime_instance: RuntimeComponentInstanceIndex,
4223 thread_idx: u32,
4224 how: ResumeThread,
4225 ) -> Result<bool> {
4226 let thread_id =
4227 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4228 let state = store.concurrent_state_mut()?;
4229 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4230
4231 if store.current_guest_thread()? == guest_thread {
4232 bail!(Trap::CannotResumeThread);
4233 }
4234
4235 let priority = match how {
4236 ResumeThread::Resume => Priority::Switch,
4237 ResumeThread::ResumeLater => Priority::Low,
4238
4239 ResumeThread::Promote => {
4242 let instance = self.runtime_instance(runtime_instance);
4243 let do_not_enter = store
4244 .instance_state(instance)
4245 .concurrent_state()
4246 .do_not_enter;
4247 return store.concurrent_state_mut()?.promote_work_item_matching(
4248 |item: &WorkItem| match item {
4249 WorkItem::ResumeThread { thread, .. }
4250 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4251
4252 WorkItem::GuestCall {
4253 call: GuestCall { thread, kind },
4254 ..
4255 } => {
4256 *thread == guest_thread
4257 && match kind {
4258 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4262
4263 GuestCallKind::StartExplicit(_) => true,
4266
4267 GuestCallKind::StartImplicit(_) => false,
4271 }
4272 }
4273
4274 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4275 },
4276 );
4277 }
4278 };
4279
4280 let state = store.concurrent_state_mut()?;
4281 let thread = state.get_mut(guest_thread.thread)?;
4282
4283 match &thread.state {
4286 GuestThreadState::NotStartedExplicit(_) => {}
4287 GuestThreadState::Suspended(_) => {
4288 log::trace!("resuming thread {thread_id:?} that was suspended");
4289 state.schedule_suspended_thread(
4290 guest_thread,
4291 self.runtime_instance(runtime_instance),
4292 priority,
4293 )?;
4294 return Ok(true);
4295 }
4296 _ => bail!(Trap::CannotResumeThread),
4297 }
4298 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4299 GuestThreadState::NotStartedExplicit(start_func) => {
4300 log::trace!("starting thread {guest_thread:?}");
4301 let guest_call = WorkItem::GuestCall {
4302 instance: self.runtime_instance(runtime_instance),
4303 call: GuestCall {
4304 thread: guest_thread,
4305 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4306 start_func(store, guest_thread)
4307 })),
4308 },
4309 };
4310 state.push_work_item(guest_call, priority)?;
4311 }
4312 other => {
4313 thread.state = other;
4314 bail_bug!("thread state checked to be resumable above");
4315 }
4316 }
4317 Ok(true)
4318 }
4319
4320 fn add_guest_thread_to_instance_table(
4321 self,
4322 thread_id: TableId<GuestThread>,
4323 store: &mut StoreOpaque,
4324 runtime_instance: RuntimeComponentInstanceIndex,
4325 ) -> Result<u32> {
4326 let guest_id = store
4327 .instance_state(self.runtime_instance(runtime_instance))
4328 .thread_handle_table()
4329 .guest_thread_insert(thread_id.rep())?;
4330 store
4331 .concurrent_state_mut()?
4332 .get_mut(thread_id)?
4333 .instance_rep = Some(guest_id);
4334 Ok(guest_id)
4335 }
4336
4337 pub(crate) fn suspension_intrinsic(
4341 self,
4342 store: &mut StoreOpaque,
4343 caller: RuntimeComponentInstanceIndex,
4344 yielding: bool,
4345 to_thread: SuspensionTarget,
4346 ) -> Result<WaitResult> {
4347 let check_suspend = match to_thread {
4348 SuspensionTarget::Promote(thread) => {
4349 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4350 }
4351 SuspensionTarget::Resume(thread) => {
4352 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4353 bail_bug!(
4354 "`resume_thread` should only ever return false \
4355 when `ResumeThread::Promote` is passed to it"
4356 );
4357 }
4358 false
4359 }
4360 SuspensionTarget::None => true,
4361 };
4362
4363 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4364 return if yielding {
4365 Ok(WaitResult::Completed)
4366 } else {
4367 Err(Trap::CannotBlockSyncTask.into())
4368 };
4369 }
4370
4371 let guest_thread = store.current_guest_thread()?;
4372
4373 let reason = if yielding {
4374 SuspendReason::Yielding {
4375 thread: guest_thread,
4376 }
4377 } else {
4378 SuspendReason::ExplicitlySuspending {
4379 thread: guest_thread,
4380 }
4381 };
4382
4383 store.suspend(reason)?;
4384
4385 Ok(WaitResult::Completed)
4386 }
4387
4388 fn waitable_check(
4390 self,
4391 store: &mut StoreOpaque,
4392 caller: RuntimeInstance,
4393 check: WaitableCheck,
4394 params: WaitableCheckParams,
4395 ) -> Result<u32> {
4396 let guest_thread = store.current_guest_thread()?;
4397
4398 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4399
4400 match &check {
4403 WaitableCheck::Wait => {
4404 let set = params.set;
4405
4406 loop {
4411 let state = store.concurrent_state_mut()?;
4412 let task = state.get_mut(guest_thread.task)?;
4413 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4414 || !state.get_mut(set)?.ready.is_empty()
4415 {
4416 break;
4417 }
4418
4419 store.switch_or_trap_if_may_not_suspend(caller)?;
4420
4421 store.suspend(SuspendReason::Waiting {
4422 set,
4423 thread: guest_thread,
4424 })?;
4425 }
4426 }
4427 WaitableCheck::Poll => {}
4428 }
4429
4430 log::trace!(
4431 "waitable check for {guest_thread:?}; set {:?}, part two",
4432 params.set
4433 );
4434
4435 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4437
4438 let (ordinal, handle, result) = match &check {
4439 WaitableCheck::Wait => {
4440 let (event, waitable) = match event {
4441 Some(p) => p,
4442 None => bail_bug!("event expected to be present"),
4443 };
4444 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4445 let (ordinal, result) = event.parts();
4446 (ordinal, handle, result)
4447 }
4448 WaitableCheck::Poll => {
4449 if let Some((event, waitable)) = event {
4450 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4451 let (ordinal, result) = event.parts();
4452 (ordinal, handle, result)
4453 } else {
4454 log::trace!(
4455 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4456 guest_thread.task,
4457 params.set
4458 );
4459 let (ordinal, result) = Event::None.parts();
4460 (ordinal, 0, result)
4461 }
4462 }
4463 };
4464 let memory = self.options_memory_mut(store, params.options);
4465 let ptr = crate::component::func::validate_inbounds_dynamic(
4466 &CanonicalAbiInfo::POINTER_PAIR,
4467 memory,
4468 &ValRaw::u32(params.payload),
4469 )?;
4470 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4471 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4472 Ok(ordinal)
4473 }
4474
4475 pub(crate) fn subtask_cancel(
4477 self,
4478 store: &mut StoreOpaque,
4479 caller_instance: RuntimeComponentInstanceIndex,
4480 async_: bool,
4481 task_id: u32,
4482 ) -> Result<u32> {
4483 let (rep, is_host) = store
4484 .instance_state(self.runtime_instance(caller_instance))
4485 .handle_table()
4486 .subtask_rep(task_id)?;
4487 let waitable = if is_host {
4488 Waitable::Host(TableId::<HostTask>::new(rep))
4489 } else {
4490 Waitable::Guest(TableId::<GuestTask>::new(rep))
4491 };
4492 let concurrent_state = store.concurrent_state_mut()?;
4493
4494 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4495
4496 waitable.trap_if_in_waitable_set(concurrent_state)?;
4497
4498 let needs_block;
4499 if let Waitable::Host(host_task) = waitable {
4500 let state = &mut concurrent_state.get_mut(host_task)?.state;
4501 match state {
4502 HostTaskState::CalleeRunning(handle) => {
4509 handle.abort();
4510 *state = HostTaskState::CalleeCancelling;
4511 needs_block = true;
4512 }
4513
4514 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4517 bail!(Trap::SubtaskCancelAfterTerminal);
4518 }
4519 HostTaskState::CalleeDone { cancelled: false } => {
4520 *state = HostTaskState::CalleeDone { cancelled: true };
4523 needs_block = false;
4524 }
4525
4526 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4529 bail_bug!("invalid states for host callee")
4530 }
4531 }
4532 } else {
4533 let guest_task = TableId::<GuestTask>::new(rep);
4534 let task = concurrent_state.get_mut(guest_task)?;
4535
4536 if task.cancel_requested {
4539 bail!(Trap::SubtaskCancelAfterTerminal);
4540 }
4541
4542 if !task.already_lowered_parameters() {
4543 store.cancel_guest_subtask_without_lowered_parameters(
4544 self.runtime_instance(caller_instance),
4545 guest_task,
4546 )?;
4547 return Ok(Status::StartCancelled as u32);
4548 } else if !task.returned_or_cancelled() {
4549 task.cancel_requested = true;
4556 task.event = Some(Event::Cancelled);
4557 let runtime_instance = task.instance;
4558 for thread in task.threads.clone() {
4559 let thread = QualifiedThreadId {
4560 task: guest_task,
4561 thread,
4562 };
4563 let concurrent_state = store.concurrent_state_mut()?;
4564 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4565
4566 let yield_ = |store: &mut StoreOpaque| {
4567 let state = store.instance_state(runtime_instance).concurrent_state();
4572 let old_do_not_suspend = state.do_not_suspend;
4573 state.do_not_suspend = false;
4574
4575 let caller = store.current_guest_thread()?;
4576
4577 let state = store.concurrent_state_mut()?;
4582 let set = state.get_mut(caller.thread)?.sync_call_set;
4583 waitable.join(state, Some(set))?;
4584
4585 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4586
4587 let state = store.concurrent_state_mut()?;
4588 waitable.join(state, None)?;
4589
4590 store
4591 .instance_state(runtime_instance)
4592 .concurrent_state()
4593 .do_not_suspend = old_do_not_suspend;
4594
4595 Ok::<(), crate::Error>(())
4596 };
4597
4598 match thread_mut.wake_on_cancel.take() {
4599 WakeOnCancel::Waiting(set) => {
4600 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4602 let item = WorkItem::GuestCall {
4603 instance: runtime_instance,
4604 call: GuestCall {
4605 thread,
4606 kind: GuestCallKind::DeliverEvent {
4607 instance,
4608 set: Some(set),
4609 },
4610 },
4611 };
4612 concurrent_state.set_switch_item(item)?;
4613
4614 yield_(store)?;
4615
4616 break;
4617 }
4618 WakeOnCancel::Yielding => {
4619 if concurrent_state.promote_thread_work_item(thread)? {
4620 yield_(store)?;
4621 break;
4622 } else if store
4623 .instance_state(runtime_instance)
4624 .concurrent_state()
4625 .pending
4626 .contains_key(&thread)
4627 {
4628 store
4638 .concurrent_state_mut()?
4639 .get_mut(thread.thread)?
4640 .wake_on_cancel = WakeOnCancel::Yielding;
4641 } else {
4642 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4643 }
4644 }
4645 WakeOnCancel::None => {}
4646 }
4647 }
4648
4649 needs_block = !store
4652 .concurrent_state_mut()?
4653 .get_mut(guest_task)?
4654 .returned_or_cancelled()
4655 } else {
4656 needs_block = false;
4657 }
4658 };
4659
4660 if needs_block {
4664 if async_ {
4665 return Ok(BLOCKED);
4666 }
4667
4668 let old_next_switch_item = store.concurrent_state_mut()?.save_next_switch_item()?;
4671
4672 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4675
4676 let state = store.concurrent_state_mut()?;
4677 state.restore_next_switch_item(old_next_switch_item)?;
4678
4679 }
4681
4682 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4683 if let Some(Event::Subtask {
4684 status: status @ (Status::Returned | Status::ReturnCancelled),
4685 }) = event
4686 {
4687 Ok(status as u32)
4688 } else {
4689 bail!(Trap::SubtaskCancelAfterTerminal);
4690 }
4691 }
4692}
4693
4694pub trait VMComponentAsyncStore {
4702 unsafe fn prepare_call(
4708 &mut self,
4709 instance: Instance,
4710 memory: *mut VMMemoryDefinition,
4711 start: NonNull<VMFuncRef>,
4712 return_: NonNull<VMFuncRef>,
4713 caller_instance: RuntimeComponentInstanceIndex,
4714 callee_instance: RuntimeComponentInstanceIndex,
4715 task_return_type: TypeTupleIndex,
4716 callee_async: bool,
4717 string_encoding: StringEncoding,
4718 result_count: u32,
4719 storage: *mut ValRaw,
4720 storage_len: usize,
4721 ) -> Result<()>;
4722
4723 unsafe fn start_call(
4726 &mut self,
4727 instance: Instance,
4728 callback: *mut VMFuncRef,
4729 post_return: *mut VMFuncRef,
4730 callee: NonNull<VMFuncRef>,
4731 param_count: u32,
4732 result_count: u32,
4733 flags: u32,
4734 storage: *mut MaybeUninit<ValRaw>,
4735 storage_len: usize,
4736 ) -> Result<()>;
4737
4738 fn future_write(
4740 &mut self,
4741 instance: Instance,
4742 caller: RuntimeComponentInstanceIndex,
4743 ty: TypeFutureTableIndex,
4744 options: OptionsIndex,
4745 future: u32,
4746 address: u32,
4747 ) -> Result<u32>;
4748
4749 fn future_read(
4751 &mut self,
4752 instance: Instance,
4753 caller: RuntimeComponentInstanceIndex,
4754 ty: TypeFutureTableIndex,
4755 options: OptionsIndex,
4756 future: u32,
4757 address: u32,
4758 ) -> Result<u32>;
4759
4760 fn future_drop_writable(
4762 &mut self,
4763 instance: Instance,
4764 ty: TypeFutureTableIndex,
4765 writer: u32,
4766 ) -> Result<()>;
4767
4768 fn stream_write(
4770 &mut self,
4771 instance: Instance,
4772 caller: RuntimeComponentInstanceIndex,
4773 ty: TypeStreamTableIndex,
4774 options: OptionsIndex,
4775 stream: u32,
4776 address: u32,
4777 count: u32,
4778 ) -> Result<u32>;
4779
4780 fn stream_read(
4782 &mut self,
4783 instance: Instance,
4784 caller: RuntimeComponentInstanceIndex,
4785 ty: TypeStreamTableIndex,
4786 options: OptionsIndex,
4787 stream: u32,
4788 address: u32,
4789 count: u32,
4790 ) -> Result<u32>;
4791
4792 fn flat_stream_write(
4795 &mut self,
4796 instance: Instance,
4797 caller: RuntimeComponentInstanceIndex,
4798 ty: TypeStreamTableIndex,
4799 options: OptionsIndex,
4800 payload_size: u32,
4801 payload_align: u32,
4802 stream: u32,
4803 address: u32,
4804 count: u32,
4805 ) -> Result<u32>;
4806
4807 fn flat_stream_read(
4810 &mut self,
4811 instance: Instance,
4812 caller: RuntimeComponentInstanceIndex,
4813 ty: TypeStreamTableIndex,
4814 options: OptionsIndex,
4815 payload_size: u32,
4816 payload_align: u32,
4817 stream: u32,
4818 address: u32,
4819 count: u32,
4820 ) -> Result<u32>;
4821
4822 fn stream_drop_writable(
4824 &mut self,
4825 instance: Instance,
4826 ty: TypeStreamTableIndex,
4827 writer: u32,
4828 ) -> Result<()>;
4829
4830 fn error_context_debug_message(
4832 &mut self,
4833 instance: Instance,
4834 ty: TypeComponentLocalErrorContextTableIndex,
4835 options: OptionsIndex,
4836 err_ctx_handle: u32,
4837 debug_msg_address: u32,
4838 ) -> Result<()>;
4839
4840 fn thread_new_indirect(
4842 &mut self,
4843 instance: Instance,
4844 caller: RuntimeComponentInstanceIndex,
4845 func_ty_idx: ModuleInternedTypeIndex,
4846 start_func_table_idx: RuntimeTableIndex,
4847 start_func_idx: u32,
4848 context: i32,
4849 ) -> Result<u32>;
4850}
4851
4852impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4854 unsafe fn prepare_call(
4855 &mut self,
4856 instance: Instance,
4857 memory: *mut VMMemoryDefinition,
4858 start: NonNull<VMFuncRef>,
4859 return_: NonNull<VMFuncRef>,
4860 caller_instance: RuntimeComponentInstanceIndex,
4861 callee_instance: RuntimeComponentInstanceIndex,
4862 task_return_type: TypeTupleIndex,
4863 callee_async: bool,
4864 string_encoding: StringEncoding,
4865 result_count_or_max_if_async: u32,
4866 storage: *mut ValRaw,
4867 storage_len: usize,
4868 ) -> Result<()> {
4869 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4873
4874 unsafe {
4875 instance.prepare_call(
4876 StoreContextMut(self),
4877 start,
4878 return_,
4879 caller_instance,
4880 callee_instance,
4881 task_return_type,
4882 callee_async,
4883 memory,
4884 string_encoding,
4885 match result_count_or_max_if_async {
4886 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4887 params,
4888 has_result: false,
4889 },
4890 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4891 params,
4892 has_result: true,
4893 },
4894 result_count => CallerInfo::Sync {
4895 params,
4896 result_count,
4897 },
4898 },
4899 )
4900 }
4901 }
4902
4903 unsafe fn start_call(
4904 &mut self,
4905 instance: Instance,
4906 callback: *mut VMFuncRef,
4907 post_return: *mut VMFuncRef,
4908 callee: NonNull<VMFuncRef>,
4909 param_count: u32,
4910 result_count: u32,
4911 flags: u32,
4912 storage: *mut MaybeUninit<ValRaw>,
4913 storage_len: usize,
4914 ) -> Result<()> {
4915 unsafe {
4916 instance.start_call(
4917 StoreContextMut(self),
4918 callback,
4919 post_return,
4920 callee,
4921 param_count,
4922 result_count,
4923 flags,
4924 core::slice::from_raw_parts_mut(storage, storage_len),
4928 )
4929 }
4930 }
4931
4932 fn future_write(
4933 &mut self,
4934 instance: Instance,
4935 caller: RuntimeComponentInstanceIndex,
4936 ty: TypeFutureTableIndex,
4937 options: OptionsIndex,
4938 future: u32,
4939 address: u32,
4940 ) -> Result<u32> {
4941 instance
4942 .guest_write(
4943 StoreContextMut(self),
4944 caller,
4945 TransmitIndex::Future(ty),
4946 options,
4947 None,
4948 future,
4949 address,
4950 1,
4951 )
4952 .map(|result| result.encode())
4953 }
4954
4955 fn future_read(
4956 &mut self,
4957 instance: Instance,
4958 caller: RuntimeComponentInstanceIndex,
4959 ty: TypeFutureTableIndex,
4960 options: OptionsIndex,
4961 future: u32,
4962 address: u32,
4963 ) -> Result<u32> {
4964 instance
4965 .guest_read(
4966 StoreContextMut(self),
4967 caller,
4968 TransmitIndex::Future(ty),
4969 options,
4970 None,
4971 future,
4972 address,
4973 1,
4974 )
4975 .map(|result| result.encode())
4976 }
4977
4978 fn stream_write(
4979 &mut self,
4980 instance: Instance,
4981 caller: RuntimeComponentInstanceIndex,
4982 ty: TypeStreamTableIndex,
4983 options: OptionsIndex,
4984 stream: u32,
4985 address: u32,
4986 count: u32,
4987 ) -> Result<u32> {
4988 instance
4989 .guest_write(
4990 StoreContextMut(self),
4991 caller,
4992 TransmitIndex::Stream(ty),
4993 options,
4994 None,
4995 stream,
4996 address,
4997 count,
4998 )
4999 .map(|result| result.encode())
5000 }
5001
5002 fn stream_read(
5003 &mut self,
5004 instance: Instance,
5005 caller: RuntimeComponentInstanceIndex,
5006 ty: TypeStreamTableIndex,
5007 options: OptionsIndex,
5008 stream: u32,
5009 address: u32,
5010 count: u32,
5011 ) -> Result<u32> {
5012 instance
5013 .guest_read(
5014 StoreContextMut(self),
5015 caller,
5016 TransmitIndex::Stream(ty),
5017 options,
5018 None,
5019 stream,
5020 address,
5021 count,
5022 )
5023 .map(|result| result.encode())
5024 }
5025
5026 fn future_drop_writable(
5027 &mut self,
5028 instance: Instance,
5029 ty: TypeFutureTableIndex,
5030 writer: u32,
5031 ) -> Result<()> {
5032 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5033 }
5034
5035 fn flat_stream_write(
5036 &mut self,
5037 instance: Instance,
5038 caller: RuntimeComponentInstanceIndex,
5039 ty: TypeStreamTableIndex,
5040 options: OptionsIndex,
5041 payload_size: u32,
5042 payload_align: u32,
5043 stream: u32,
5044 address: u32,
5045 count: u32,
5046 ) -> Result<u32> {
5047 instance
5048 .guest_write(
5049 StoreContextMut(self),
5050 caller,
5051 TransmitIndex::Stream(ty),
5052 options,
5053 Some(FlatAbi {
5054 size: payload_size,
5055 align: payload_align,
5056 }),
5057 stream,
5058 address,
5059 count,
5060 )
5061 .map(|result| result.encode())
5062 }
5063
5064 fn flat_stream_read(
5065 &mut self,
5066 instance: Instance,
5067 caller: RuntimeComponentInstanceIndex,
5068 ty: TypeStreamTableIndex,
5069 options: OptionsIndex,
5070 payload_size: u32,
5071 payload_align: u32,
5072 stream: u32,
5073 address: u32,
5074 count: u32,
5075 ) -> Result<u32> {
5076 instance
5077 .guest_read(
5078 StoreContextMut(self),
5079 caller,
5080 TransmitIndex::Stream(ty),
5081 options,
5082 Some(FlatAbi {
5083 size: payload_size,
5084 align: payload_align,
5085 }),
5086 stream,
5087 address,
5088 count,
5089 )
5090 .map(|result| result.encode())
5091 }
5092
5093 fn stream_drop_writable(
5094 &mut self,
5095 instance: Instance,
5096 ty: TypeStreamTableIndex,
5097 writer: u32,
5098 ) -> Result<()> {
5099 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5100 }
5101
5102 fn error_context_debug_message(
5103 &mut self,
5104 instance: Instance,
5105 ty: TypeComponentLocalErrorContextTableIndex,
5106 options: OptionsIndex,
5107 err_ctx_handle: u32,
5108 debug_msg_address: u32,
5109 ) -> Result<()> {
5110 instance.error_context_debug_message(
5111 StoreContextMut(self),
5112 ty,
5113 options,
5114 err_ctx_handle,
5115 debug_msg_address,
5116 )
5117 }
5118
5119 fn thread_new_indirect(
5120 &mut self,
5121 instance: Instance,
5122 caller: RuntimeComponentInstanceIndex,
5123 func_ty_idx: ModuleInternedTypeIndex,
5124 start_func_table_idx: RuntimeTableIndex,
5125 start_func_idx: u32,
5126 context: i32,
5127 ) -> Result<u32> {
5128 instance.thread_new_indirect(
5129 StoreContextMut(self),
5130 caller,
5131 func_ty_idx,
5132 start_func_table_idx,
5133 start_func_idx,
5134 context,
5135 )
5136 }
5137}
5138
5139type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5140
5141async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5144where
5145 F: Future,
5146{
5147 let mut future = pin!(future);
5148 future::poll_fn(|cx| {
5149 let old_thread = match tls::get(|store| store.set_thread(task)) {
5150 Ok(thread) => thread,
5151 Err(error) => return Poll::Ready(Err(error)),
5152 };
5153 let result = future.as_mut().poll(cx);
5154 match tls::get(|store| store.set_thread(old_thread)) {
5155 Ok(_) => result.map(Ok),
5156 Err(error) => Poll::Ready(Err(error)),
5157 }
5158 })
5159 .await
5160}
5161
5162pub(crate) struct HostTask {
5166 common: WaitableCommon,
5167
5168 call_context: CallContext,
5171
5172 state: HostTaskState,
5173
5174 group: TaskGroupId,
5175}
5176
5177enum HostTaskState {
5178 CalleeStarted,
5183
5184 CalleeRunning(JoinHandle),
5189
5190 CalleeCancelling,
5194
5195 CalleeFinished(LiftedResult),
5199
5200 CalleeDone { cancelled: bool },
5203}
5204
5205impl HostTask {
5206 fn new(
5207 concurrent_state: &mut ConcurrentState,
5208 state: HostTaskState,
5209 caller: QualifiedThreadId,
5210 ) -> Result<Self> {
5211 let group = concurrent_state.get_mut(caller.task)?.group;
5212 concurrent_state.increment_group_ref_count(group)?;
5213
5214 Ok(Self {
5215 common: WaitableCommon::default(),
5216 call_context: CallContext::default(),
5217 state,
5218 group,
5219 })
5220 }
5221}
5222
5223impl TableDebug for HostTask {
5224 fn type_name() -> &'static str {
5225 "HostTask"
5226 }
5227}
5228
5229type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5230
5231enum Caller {
5233 Host {
5235 tx: Option<oneshot::Sender<LiftedResult>>,
5237 caller: Option<TableId<HostTask>>,
5241 },
5242 Guest {
5244 thread: QualifiedThreadId,
5246 },
5247}
5248
5249struct LiftResult {
5252 lift: RawLift,
5253 ty: TypeTupleIndex,
5254 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5255 string_encoding: StringEncoding,
5256}
5257
5258#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5263pub(crate) struct QualifiedThreadId {
5264 task: TableId<GuestTask>,
5265 thread: TableId<GuestThread>,
5266}
5267
5268impl QualifiedThreadId {
5269 fn qualify(
5270 state: &mut ConcurrentState,
5271 thread: TableId<GuestThread>,
5272 ) -> Result<QualifiedThreadId> {
5273 Ok(QualifiedThreadId {
5274 task: state.get_mut(thread)?.parent_task,
5275 thread,
5276 })
5277 }
5278}
5279
5280impl fmt::Debug for QualifiedThreadId {
5281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5282 f.debug_tuple("QualifiedThreadId")
5283 .field(&self.task.rep())
5284 .field(&self.thread.rep())
5285 .finish()
5286 }
5287}
5288
5289enum GuestThreadState {
5290 NotStartedImplicit,
5291 NotStartedExplicit(
5292 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5293 ),
5294 Running,
5295 Suspended(StoreFiber<'static>),
5296 Ready {
5297 fiber: StoreFiber<'static>,
5298 },
5299 Completed,
5300}
5301
5302impl GuestThreadState {
5303 fn check_no_fiber(&self) -> Result<()> {
5304 match self {
5305 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5306 Self::NotStartedImplicit
5307 | Self::NotStartedExplicit(_)
5308 | Self::Running
5309 | Self::Completed => Ok(()),
5310 }
5311 }
5312}
5313
5314impl fmt::Debug for GuestThreadState {
5315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5316 match self {
5317 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5318 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5319 Self::Running => f.debug_tuple("Running").finish(),
5320 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5321 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5322 Self::Completed => f.debug_tuple("Completed").finish(),
5323 }
5324 }
5325}
5326
5327#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5328enum WakeOnCancel {
5329 None,
5330 Waiting(TableId<WaitableSet>),
5331 Yielding,
5332}
5333
5334impl WakeOnCancel {
5335 fn is_none(self) -> bool {
5336 matches!(self, WakeOnCancel::None)
5337 }
5338
5339 fn replace(&mut self, other: WakeOnCancel) -> Self {
5340 let old = *self;
5341 *self = other;
5342 old
5343 }
5344
5345 fn take(&mut self) -> Self {
5346 self.replace(WakeOnCancel::None)
5347 }
5348}
5349
5350pub struct GuestThread {
5351 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5354 parent_task: TableId<GuestTask>,
5356 wake_on_cancel: WakeOnCancel,
5359 state: GuestThreadState,
5361 instance_rep: Option<u32>,
5364 sync_call_set: TableId<WaitableSet>,
5366 old_do_not_suspend: Option<bool>,
5369}
5370
5371impl GuestThread {
5372 fn from_instance(
5375 state: Pin<&mut ComponentInstance>,
5376 caller_instance: RuntimeComponentInstanceIndex,
5377 guest_thread: u32,
5378 ) -> Result<TableId<Self>> {
5379 let rep = state.instance_states().0[caller_instance]
5380 .thread_handle_table()
5381 .guest_thread_rep(guest_thread)?;
5382 Ok(TableId::new(rep))
5383 }
5384
5385 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5386 let sync_call_set = state.push(WaitableSet {
5387 is_sync_call_set: true,
5388 ..WaitableSet::default()
5389 })?;
5390 Ok(Self {
5391 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5392 parent_task,
5393 wake_on_cancel: WakeOnCancel::None,
5394 state: GuestThreadState::NotStartedImplicit,
5395 instance_rep: None,
5396 sync_call_set,
5397 old_do_not_suspend: None,
5398 })
5399 }
5400
5401 fn new_explicit(
5402 state: &mut ConcurrentState,
5403 parent_task: TableId<GuestTask>,
5404 start_func: Box<
5405 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5406 >,
5407 ) -> Result<Self> {
5408 state.increment_task_ref_count(parent_task)?;
5409 let sync_call_set = state.push(WaitableSet {
5410 is_sync_call_set: true,
5411 ..WaitableSet::default()
5412 })?;
5413 Ok(Self {
5414 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5415 parent_task,
5416 wake_on_cancel: WakeOnCancel::None,
5417 state: GuestThreadState::NotStartedExplicit(start_func),
5418 instance_rep: None,
5419 sync_call_set,
5420 old_do_not_suspend: None,
5421 })
5422 }
5423}
5424
5425impl TableDebug for GuestThread {
5426 fn type_name() -> &'static str {
5427 "GuestThread"
5428 }
5429}
5430
5431enum SyncResult {
5432 NotProduced,
5433 Produced(Option<ValRaw>),
5434 Taken,
5435}
5436
5437impl SyncResult {
5438 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5439 Ok(match mem::replace(self, SyncResult::Taken) {
5440 SyncResult::NotProduced => None,
5441 SyncResult::Produced(val) => Some(val),
5442 SyncResult::Taken => {
5443 bail_bug!("attempted to take a synchronous result that was already taken")
5444 }
5445 })
5446 }
5447}
5448
5449pub(crate) struct GuestTask {
5451 common: WaitableCommon,
5453 lower_params: Option<RawLower>,
5455 lift_result: Option<LiftResult>,
5457 result: Option<LiftedResult>,
5460 callback: Option<CallbackFn>,
5463 caller: Caller,
5465 call_context: CallContext,
5470 sync_result: SyncResult,
5473 cancel_request_delivered: bool,
5477 cancel_requested: bool,
5482 starting_sent: bool,
5485 instance: RuntimeInstance,
5492 event: Option<Event>,
5494 exited: bool,
5496 threads: HashSet<TableId<GuestThread>>,
5498 async_typed: bool,
5501 async_lifted: bool,
5504 decremented_interesting_task_count: bool,
5507 group: TaskGroupId,
5509 ref_count: usize,
5514}
5515
5516impl GuestTask {
5517 fn already_lowered_parameters(&self) -> bool {
5518 self.lower_params.is_none()
5520 }
5521
5522 fn returned_or_cancelled(&self) -> bool {
5523 self.lift_result.is_none()
5525 }
5526
5527 fn new(
5528 state: &mut ConcurrentState,
5529 lower_params: RawLower,
5530 lift_result: LiftResult,
5531 caller: Caller,
5532 callback: Option<CallbackFn>,
5533 instance: RuntimeInstance,
5534 async_typed: bool,
5535 async_lifted: bool,
5536 ) -> Result<QualifiedThreadId> {
5537 let group = match caller {
5538 Caller::Guest { thread } => {
5539 let group = state.get_mut(thread.task)?.group;
5540 state.increment_group_ref_count(group)?;
5541 group
5542 }
5543 Caller::Host { .. } => state.make_task_group()?,
5544 };
5545
5546 let task = state.push(Self {
5547 common: WaitableCommon::default(),
5548 lower_params: Some(lower_params),
5549 lift_result: Some(lift_result),
5550 result: None,
5551 callback,
5552 caller,
5553 call_context: CallContext::default(),
5554 sync_result: SyncResult::NotProduced,
5555 cancel_request_delivered: false,
5556 cancel_requested: false,
5557 starting_sent: false,
5558 instance,
5559 event: None,
5560 exited: false,
5561 threads: HashSet::new(),
5562 async_typed,
5563 async_lifted,
5564 decremented_interesting_task_count: false,
5565 group,
5566 ref_count: 2,
5569 })?;
5570 let new_thread = GuestThread::new_implicit(state, task)?;
5571 let thread = state.push(new_thread)?;
5572 state.get_mut(task)?.threads.insert(thread);
5573 state.interesting_tasks += 1;
5574 let thread = QualifiedThreadId { task, thread };
5575 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5576 Ok(thread)
5577 }
5578}
5579
5580impl TableDebug for GuestTask {
5581 fn type_name() -> &'static str {
5582 "GuestTask"
5583 }
5584}
5585
5586#[derive(Default)]
5588struct WaitableCommon {
5589 event: Option<Event>,
5591 set: Option<TableId<WaitableSet>>,
5593 handle: Option<u32>,
5595}
5596
5597#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5599enum Waitable {
5600 Host(TableId<HostTask>),
5602 Guest(TableId<GuestTask>),
5604 Transmit(TableId<TransmitHandle>),
5606}
5607
5608impl Waitable {
5609 fn from_instance(
5612 state: Pin<&mut ComponentInstance>,
5613 caller_instance: RuntimeComponentInstanceIndex,
5614 waitable: u32,
5615 ) -> Result<Self> {
5616 use crate::runtime::vm::component::Waitable;
5617
5618 let (waitable, kind) = state.instance_states().0[caller_instance]
5619 .handle_table()
5620 .waitable_rep(waitable)?;
5621
5622 Ok(match kind {
5623 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5624 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5625 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5626 })
5627 }
5628
5629 fn rep(&self) -> u32 {
5631 match self {
5632 Self::Host(id) => id.rep(),
5633 Self::Guest(id) => id.rep(),
5634 Self::Transmit(id) => id.rep(),
5635 }
5636 }
5637
5638 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5642 log::trace!("waitable {self:?} join set {set:?}");
5643
5644 let old = mem::replace(&mut self.common(state)?.set, set);
5645
5646 if let Some(old) = old {
5647 match *self {
5648 Waitable::Host(id) => state.remove_child(id, old),
5649 Waitable::Guest(id) => state.remove_child(id, old),
5650 Waitable::Transmit(id) => state.remove_child(id, old),
5651 }?;
5652
5653 state.get_mut(old)?.ready.remove(self);
5654 }
5655
5656 if let Some(set) = set {
5657 match *self {
5658 Waitable::Host(id) => state.add_child(id, set),
5659 Waitable::Guest(id) => state.add_child(id, set),
5660 Waitable::Transmit(id) => state.add_child(id, set),
5661 }?;
5662
5663 if self.common(state)?.event.is_some() {
5664 self.mark_ready(state)?;
5665 }
5666 }
5667
5668 Ok(())
5669 }
5670
5671 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5673 Ok(match self {
5674 Self::Host(id) => &mut state.get_mut(*id)?.common,
5675 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5676 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5677 })
5678 }
5679
5680 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5686 if self.common(state)?.set.is_some() {
5687 bail!(Trap::WaitableSyncAndAsync);
5688 }
5689 Ok(())
5690 }
5691
5692 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5698 if let Some(set) = self.common(state)?.set
5699 && state.get_mut(set)?.is_sync_call_set
5700 {
5701 bail!(Trap::WaitableSyncAndAsync);
5702 }
5703 Ok(())
5704 }
5705
5706 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5710 log::trace!("set event for {self:?}: {event:?}");
5711 self.common(state)?.event = event;
5712 self.mark_ready(state)
5713 }
5714
5715 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5717 let common = self.common(state)?;
5718 let event = common.event.take();
5719 if let Some(set) = self.common(state)?.set {
5720 state.get_mut(set)?.ready.remove(self);
5721 }
5722
5723 Ok(event)
5724 }
5725
5726 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5730 if let Some(set) = self.common(state)?.set {
5731 state.get_mut(set)?.ready.insert(*self);
5732 state.wake_waiter(set)?;
5733 }
5734 Ok(())
5735 }
5736
5737 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5740 match self {
5741 Self::Host(task) => state.delete_host_task(*task),
5742 Self::Guest(task) => state.decrement_task_ref_count(*task),
5743 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5744 }
5745 }
5746}
5747
5748impl fmt::Debug for Waitable {
5749 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5750 match self {
5751 Self::Host(id) => write!(f, "{id:?}"),
5752 Self::Guest(id) => write!(f, "{id:?}"),
5753 Self::Transmit(id) => write!(f, "{id:?}"),
5754 }
5755 }
5756}
5757
5758#[derive(Default)]
5760struct WaitableSet {
5761 ready: BTreeSet<Waitable>,
5763 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5765 num_waiting: usize,
5768 is_sync_call_set: bool,
5771}
5772
5773impl WaitableSet {
5774 fn stop_waiting(&mut self) -> Result<()> {
5776 self.num_waiting = match self.num_waiting.checked_sub(1) {
5777 Some(n) => n,
5778 None => bail_bug!("waiter not accounted for in waitable set"),
5779 };
5780 Ok(())
5781 }
5782}
5783
5784impl TableDebug for WaitableSet {
5785 fn type_name() -> &'static str {
5786 "WaitableSet"
5787 }
5788}
5789
5790type RawLower =
5792 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5793
5794type RawLift = Box<
5796 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5797>;
5798
5799type LiftedResult = Box<dyn Any + Send + Sync>;
5803
5804struct DummyResult;
5807
5808#[derive(Default)]
5810pub struct ConcurrentInstanceState {
5811 backpressure: u16,
5813 do_not_enter: bool,
5815 do_not_suspend: bool,
5818 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5821}
5822
5823impl ConcurrentInstanceState {
5824 pub fn pending_is_empty(&self) -> bool {
5825 self.pending.is_empty()
5826 }
5827}
5828
5829#[derive(Debug, Copy, Clone)]
5830pub(crate) enum CurrentThread {
5831 Guest(QualifiedThreadId),
5834 Host(TableId<HostTask>),
5836 DeferredHost(QualifiedThreadId),
5839 None,
5842}
5843
5844impl CurrentThread {
5845 fn guest(&self) -> Option<&QualifiedThreadId> {
5846 match self {
5847 Self::Guest(id) => Some(id),
5848 _ => None,
5849 }
5850 }
5851
5852 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5853 match self {
5854 Self::Guest(id) => Some(id.task),
5855 _ => None,
5856 }
5857 }
5858
5859 fn is_none(&self) -> bool {
5860 matches!(self, Self::None)
5861 }
5862}
5863
5864impl From<QualifiedThreadId> for CurrentThread {
5865 fn from(id: QualifiedThreadId) -> Self {
5866 Self::Guest(id)
5867 }
5868}
5869
5870impl From<TableId<HostTask>> for CurrentThread {
5871 fn from(id: TableId<HostTask>) -> Self {
5872 Self::Host(id)
5873 }
5874}
5875
5876enum Priority {
5877 Switch,
5878 High,
5879 Low,
5880}
5881
5882pub struct ConcurrentState {
5884 unforced_current_thread: CurrentThread,
5890
5891 deferred_host_call_context: Option<CallContext>,
5897
5898 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5903 table: AlwaysMut<ResourceTable>,
5905 switch_item: Option<WorkItem>,
5913 next_switch_item: Option<WorkItem>,
5919 high_priority: VecDeque<WorkItem>,
5921 low_priority: VecDeque<WorkItem>,
5923 suspend_reason: Option<SuspendReason>,
5927 worker: Option<StoreFiber<'static>>,
5931 worker_item: Option<WorkerItem>,
5933
5934 global_error_context_ref_counts:
5947 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5948
5949 interesting_tasks: usize,
5962
5963 interesting_tasks_empty_waker: Option<Waker>,
5967
5968 ready_for_concurrent_call_waker: Option<Waker>,
5973
5974 event_loop_running: bool,
5976
5977 saved_next_switch_items: Vec<Option<WorkItem>>,
5980
5981 #[cfg(feature = "task-group-hook")]
5983 task_group_hook: Option<Box<dyn TaskGroupHook>>,
5984}
5985
5986impl Default for ConcurrentState {
5987 fn default() -> Self {
5988 Self {
5989 unforced_current_thread: CurrentThread::None,
5990 deferred_host_call_context: None,
5991 table: AlwaysMut::new(ResourceTable::new()),
5992 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5993 switch_item: None,
5994 next_switch_item: None,
5995 high_priority: VecDeque::new(),
5996 low_priority: VecDeque::new(),
5997 suspend_reason: None,
5998 worker: None,
5999 worker_item: None,
6000 global_error_context_ref_counts: BTreeMap::new(),
6001 interesting_tasks: 0,
6002 interesting_tasks_empty_waker: None,
6003 ready_for_concurrent_call_waker: None,
6004 event_loop_running: false,
6005 saved_next_switch_items: Vec::new(),
6006 #[cfg(feature = "task-group-hook")]
6007 task_group_hook: None,
6008 }
6009 }
6010}
6011
6012impl ConcurrentState {
6013 pub(crate) fn take_fibers_and_futures(
6030 &mut self,
6031 fibers: &mut Vec<StoreFiber<'static>>,
6032 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6033 ) {
6034 let ConcurrentState {
6035 table,
6036 worker,
6037 switch_item,
6038 next_switch_item,
6039 high_priority,
6040 low_priority,
6041 saved_next_switch_items,
6042 futures: pending_futures,
6043 worker_item: _,
6044 unforced_current_thread: _,
6045 deferred_host_call_context: _,
6046 suspend_reason: _,
6047 global_error_context_ref_counts: _,
6048 interesting_tasks: _,
6049 interesting_tasks_empty_waker: _,
6050 ready_for_concurrent_call_waker: _,
6051 event_loop_running: _,
6052 #[cfg(feature = "task-group-hook")]
6053 task_group_hook: _,
6054 } = self;
6055
6056 let mut items = Vec::new();
6057 for (_, entry) in table.get_mut().iter_mut() {
6058 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6059 for mode in mem::take(&mut set.waiting).into_values() {
6060 match mode {
6061 WaitMode::Fiber(fiber) => {
6062 fibers.push(fiber);
6063 }
6064 WaitMode::Callback(_) => {}
6065 }
6066 }
6067 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6068 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6069 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6070 fibers.push(fiber);
6071 }
6072 GuestThreadState::NotStartedImplicit
6073 | GuestThreadState::NotStartedExplicit(_)
6074 | GuestThreadState::Running
6075 | GuestThreadState::Completed => {}
6076 }
6077 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6078 if let Some(item) = item.take() {
6079 items.push(item);
6080 }
6081 }
6082 }
6083
6084 if let Some(fiber) = worker.take() {
6085 fibers.push(fiber);
6086 }
6087
6088 let mut handle_item = |item| match item {
6089 WorkItem::ResumeFiber { fiber, .. } => {
6090 fibers.push(fiber);
6091 }
6092 WorkItem::PushFuture(future) => {
6093 pending_futures
6094 .get_mut()
6095 .as_mut()
6096 .unwrap()
6097 .push(future.into_inner());
6098 }
6099 WorkItem::ResumeThread { .. }
6100 | WorkItem::GuestCall { .. }
6101 | WorkItem::WorkerFunction(_) => {}
6102 };
6103
6104 for item in items {
6105 handle_item(item);
6106 }
6107 if let Some(item) = switch_item.take() {
6108 handle_item(item);
6109 }
6110 if let Some(item) = next_switch_item.take() {
6111 handle_item(item);
6112 }
6113 for item in mem::take(high_priority) {
6114 handle_item(item);
6115 }
6116 for item in mem::take(low_priority) {
6117 handle_item(item);
6118 }
6119 for item in mem::take(saved_next_switch_items)
6120 .into_iter()
6121 .filter_map(|v| v)
6122 {
6123 handle_item(item);
6124 }
6125
6126 if let Some(them) = pending_futures.get_mut().take() {
6127 futures.push(them);
6128 }
6129 }
6130
6131 #[cfg(feature = "gc")]
6132 pub(crate) fn trace_fiber_roots(
6133 &mut self,
6134 modules: &ModuleRegistry,
6135 unwind: &dyn Unwind,
6136 gc_roots_list: &mut GcRootsList,
6137 ) {
6138 let ConcurrentState {
6139 table,
6140 worker,
6141 switch_item,
6142 next_switch_item,
6143 high_priority,
6144 low_priority,
6145 saved_next_switch_items,
6146
6147 futures: _,
6151
6152 worker_item: _,
6154 unforced_current_thread: _,
6155 deferred_host_call_context: _,
6156 suspend_reason: _,
6157 global_error_context_ref_counts: _,
6158 interesting_tasks: _,
6159 interesting_tasks_empty_waker: _,
6160 ready_for_concurrent_call_waker: _,
6161 event_loop_running: _,
6162 #[cfg(feature = "task-group-hook")]
6163 task_group_hook: _,
6164 } = self;
6165
6166 for (_, entry) in table.get_mut().iter_mut() {
6167 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6168 for mode in set.waiting.values_mut() {
6169 match mode {
6170 WaitMode::Fiber(fiber) => {
6171 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6172 }
6173 WaitMode::Callback(_) => {}
6174 }
6175 }
6176 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6177 match &mut thread.state {
6178 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6179 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6180 }
6181 GuestThreadState::NotStartedImplicit
6182 | GuestThreadState::NotStartedExplicit(_)
6183 | GuestThreadState::Running
6184 | GuestThreadState::Completed => {}
6185 }
6186 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6187 entry.downcast_mut::<Option<WorkItem>>()
6188 {
6189 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6190 }
6191 }
6192
6193 if let Some(fiber) = worker {
6194 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6195 }
6196
6197 let mut handle_item = |item: &mut WorkItem| match item {
6198 WorkItem::ResumeFiber { fiber, .. } => {
6199 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6200 }
6201 WorkItem::PushFuture(_future) => {
6202 }
6205 WorkItem::ResumeThread { .. }
6206 | WorkItem::GuestCall { .. }
6207 | WorkItem::WorkerFunction(_) => {}
6208 };
6209
6210 if let Some(item) = switch_item {
6211 handle_item(item);
6212 }
6213 if let Some(item) = next_switch_item {
6214 handle_item(item);
6215 }
6216 for item in high_priority {
6217 handle_item(item);
6218 }
6219 for item in low_priority {
6220 handle_item(item);
6221 }
6222 for item in saved_next_switch_items
6223 .iter_mut()
6224 .filter_map(|v| v.as_mut())
6225 {
6226 handle_item(item);
6227 }
6228 }
6229
6230 fn schedule_suspended_thread(
6231 &mut self,
6232 thread: QualifiedThreadId,
6233 instance: RuntimeInstance,
6234 priority: Priority,
6235 ) -> Result<()> {
6236 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6238 bail_bug!("switch item already set");
6239 }
6240 let slot = &mut self.get_mut(thread.thread)?.state;
6241 if !matches!(slot, GuestThreadState::Suspended(_)) {
6242 bail!(Trap::CannotResumeThread);
6243 }
6244 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6245 else {
6246 unreachable!()
6247 };
6248 let item = WorkItem::ResumeFiber {
6249 instance,
6250 thread,
6251 fiber,
6252 };
6253 match priority {
6254 Priority::Switch => self.switch_item = Some(item),
6255 Priority::High => self.push_high_priority(item),
6256 Priority::Low => self.push_low_priority(item),
6257 }
6258 Ok(())
6259 }
6260
6261 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6262 let slot = &mut self.get_mut(thread)?.state;
6263 slot.check_no_fiber()?;
6264 *slot = GuestThreadState::Running;
6265 Ok(())
6266 }
6267
6268 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6269 let saved = self.push(None::<WorkItem>)?;
6272 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6273 *slot = self.next_switch_item.take();
6274 Ok(saved)
6275 }
6276
6277 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6278 if self.next_switch_item.is_some() {
6279 bail_bug!("next switch item already set when restoring");
6280 }
6281 self.next_switch_item = self.delete(saved)?;
6282 Ok(())
6283 }
6284
6285 fn take_callback_waiter(
6286 &mut self,
6287 set: TableId<WaitableSet>,
6288 thread: QualifiedThreadId,
6289 ) -> Result<Instance> {
6290 let waiting = &mut self.get_mut(set)?.waiting;
6291 let instance = match waiting.get(&thread) {
6292 Some(WaitMode::Callback(instance)) => *instance,
6293 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6294 };
6295 waiting.remove(&thread);
6296 Ok(instance)
6297 }
6298
6299 fn push<V: Send + Sync + 'static>(
6300 &mut self,
6301 value: V,
6302 ) -> Result<TableId<V>, ResourceTableError> {
6303 self.table.get_mut().push(value).map(TableId::from)
6304 }
6305
6306 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6307 self.table.get_mut().get_mut(&Resource::from(id))
6308 }
6309
6310 pub fn add_child<T: 'static, U: 'static>(
6311 &mut self,
6312 child: TableId<T>,
6313 parent: TableId<U>,
6314 ) -> Result<(), ResourceTableError> {
6315 self.table
6316 .get_mut()
6317 .add_child(Resource::from(child), Resource::from(parent))
6318 }
6319
6320 pub fn remove_child<T: 'static, U: 'static>(
6321 &mut self,
6322 child: TableId<T>,
6323 parent: TableId<U>,
6324 ) -> Result<(), ResourceTableError> {
6325 self.table
6326 .get_mut()
6327 .remove_child(Resource::from(child), Resource::from(parent))
6328 }
6329
6330 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6331 self.table.get_mut().delete(Resource::from(id))
6332 }
6333
6334 fn push_future(&mut self, future: HostTaskFuture) {
6335 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6342 }
6343
6344 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6345 if self.switch_item.is_some() {
6346 self.push_high_priority(item);
6349 bail_bug!("switch item already set");
6350 }
6351
6352 self.switch_item = Some(item);
6353 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6354
6355 Ok(())
6356 }
6357
6358 fn take_next_switch_item(&mut self) -> Result<()> {
6359 if let Some(item) = self.next_switch_item.take() {
6360 self.set_switch_item(item)?;
6361 }
6362 Ok(())
6363 }
6364
6365 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6368 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6371 return Ok(());
6372 };
6373 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6374 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6375 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6376 }
6377 let instance = self.get_mut(thread.task)?.instance;
6378 self.get_mut(thread.thread)?.wake_on_cancel.take();
6379 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6380
6381 let item = match mode {
6382 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6383 instance,
6384 thread,
6385 fiber,
6386 },
6387 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6388 instance,
6389 call: GuestCall {
6390 thread,
6391 kind: GuestCallKind::DeliverEvent {
6392 instance: callback_instance,
6393 set: Some(set),
6394 },
6395 },
6396 },
6397 };
6398 self.push_high_priority(item);
6399 Ok(())
6400 }
6401
6402 fn push_high_priority(&mut self, item: WorkItem) {
6403 self.high_priority.push_front(item);
6404 log::trace!(
6405 "push high priority: {:?}",
6406 self.high_priority.front().unwrap()
6407 );
6408 }
6409
6410 fn push_low_priority(&mut self, item: WorkItem) {
6411 self.low_priority.push_front(item);
6412 log::trace!(
6413 "push low priority: {:?}",
6414 self.low_priority.front().unwrap()
6415 );
6416 }
6417
6418 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6419 match priority {
6420 Priority::Switch => self.set_switch_item(item)?,
6421 Priority::High => self.push_high_priority(item),
6422 Priority::Low => self.push_low_priority(item),
6423 }
6424
6425 Ok(())
6426 }
6427
6428 fn promote_instance_local_thread_work_item(
6429 &mut self,
6430 current_instance: RuntimeInstance,
6431 ) -> Result<bool> {
6432 log::trace!("promote thread work items for {current_instance:?}");
6433
6434 self.promote_work_item_matching(|item: &WorkItem| {
6435 let result = match item {
6436 WorkItem::ResumeThread { instance, .. }
6437 | WorkItem::ResumeFiber { instance, .. }
6438 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6439 _ => false,
6440 };
6441
6442 log::trace!("candidate {item:?}: {result}");
6443 result
6444 })
6445 }
6446
6447 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6448 self.promote_work_item_matching(|item: &WorkItem| match item {
6449 WorkItem::ResumeThread {
6450 thread: item_thread,
6451 ..
6452 }
6453 | WorkItem::GuestCall {
6454 call:
6455 GuestCall {
6456 thread: item_thread,
6457 ..
6458 },
6459 ..
6460 } => *item_thread == thread,
6461 _ => false,
6462 })
6463 }
6464
6465 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6466 where
6467 F: FnMut(&WorkItem) -> bool,
6468 {
6469 if self.switch_item.is_none() {
6476 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6477 self.switch_item = self.high_priority.remove(index);
6478 }
6479 }
6480 if self.switch_item.is_none() {
6481 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6482 self.switch_item = self.low_priority.remove(index);
6483 }
6484 }
6485 Ok(self.switch_item.is_some())
6486 }
6487
6488 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6491 match task {
6492 Scope::HostId(task) => {
6493 let task: TableId<HostTask> = TableId::new(task);
6494 Ok(&mut self.get_mut(task)?.call_context)
6495 }
6496 Scope::Id(task) => {
6497 let task: TableId<GuestTask> = TableId::new(task);
6498 Ok(&mut self.get_mut(task)?.call_context)
6499 }
6500 }
6501 }
6502
6503 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6504 self.deferred_host_call_context.as_mut()
6505 }
6506
6507 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6508 match self.futures.get_mut().as_mut() {
6509 Some(f) => Ok(f),
6510 None => bail_bug!("futures field of concurrent state is currently taken"),
6511 }
6512 }
6513
6514 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6515 self.table.get_mut()
6516 }
6517
6518 fn debug_assert_deferred_host_invariant(&self) {
6519 debug_assert_eq!(
6520 self.deferred_host_call_context.is_some(),
6521 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6522 "a deferred host thread and call context must exist together",
6523 );
6524 }
6525
6526 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6527 self.debug_assert_deferred_host_invariant();
6528 let caller = match self.unforced_current_thread {
6529 CurrentThread::DeferredHost(caller) => caller,
6530 thread => return Ok(thread),
6531 };
6532
6533 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6535 let task = self.push(task)?;
6536 let call_context = self
6537 .deferred_host_call_context
6538 .take()
6539 .expect("deferred host call context should be present");
6540 self.get_mut(task)
6541 .expect("newly inserted host task should be present")
6542 .call_context = call_context;
6543 self.unforced_current_thread = CurrentThread::Host(task);
6544 self.debug_assert_deferred_host_invariant();
6545 log::trace!("new host task materialized {task:?}");
6546 Ok(CurrentThread::Host(task))
6547 }
6548
6549 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6550 match self.materialize_host_task()? {
6551 CurrentThread::Host(id) => Ok(Some(id)),
6552 CurrentThread::None => Ok(None),
6553 CurrentThread::Guest(_) => {
6554 bail_bug!("tried to materialize a host task id from a guest thread")
6555 }
6556 CurrentThread::DeferredHost(_) => {
6557 bail_bug!(
6558 "current thread is a deferred host thread which should have been materialized"
6559 )
6560 }
6561 }
6562 }
6563
6564 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6565 match self.materialize_host_task()? {
6566 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6567 _ => bail_bug!("current scope is not a deferred host scope"),
6568 }
6569 }
6570
6571 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6572 let count = &mut self.get_mut(task)?.ref_count;
6573 *count += 1;
6574 log::trace!("increment {task:?} to {count}");
6575 Ok(())
6576 }
6577
6578 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6579 let count = &mut self.get_mut(task)?.ref_count;
6580 if *count == 0 {
6581 bail_bug!("can't decrement task ref count that's already zero");
6582 }
6583 *count -= 1;
6584 log::trace!("decrement {task:?} to {count}");
6585 if *count == 0 {
6586 let task = self.delete(task)?;
6587
6588 self.decrement_group_ref_count(task.group)?;
6589
6590 debug_assert!(task.decremented_interesting_task_count);
6597 }
6598 Ok(())
6599 }
6600
6601 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6602 log::trace!("delete host task {task:?}");
6603 let task = self.delete(task)?;
6604
6605 self.decrement_group_ref_count(task.group)
6606 }
6607}
6608
6609fn for_any_lower<
6612 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6613>(
6614 fun: F,
6615) -> F {
6616 fun
6617}
6618
6619fn for_any_lift<
6621 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6622>(
6623 fun: F,
6624) -> F {
6625 fun
6626}
6627
6628fn check_ambient_store(id: StoreId) {
6629 let message = "\
6630 `Future`s which depend on asynchronous component tasks, streams, or \
6631 futures to complete may only be polled from the event loop of the \
6632 store to which they belong. Please use \
6633 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6634 ";
6635 tls::try_get(|store| {
6636 let matched = match store {
6637 tls::TryGet::Some(store) => store.id() == id,
6638 tls::TryGet::Taken | tls::TryGet::None => false,
6639 };
6640
6641 if !matched {
6642 panic!("{message}")
6643 }
6644 });
6645}
6646
6647fn unpack_callback_code(code: u32) -> (u32, u32) {
6648 (code & 0xF, code >> 4)
6649}
6650
6651struct WaitableCheckParams {
6655 set: TableId<WaitableSet>,
6656 options: OptionsIndex,
6657 payload: u32,
6658}
6659
6660enum WaitableCheck {
6663 Wait,
6664 Poll,
6665}
6666
6667pub(crate) struct PreparedCall<R> {
6669 handle: Func,
6671 thread: QualifiedThreadId,
6673 param_count: usize,
6675 rx: oneshot::Receiver<LiftedResult>,
6678 runtime_instance: RuntimeInstance,
6680 _phantom: PhantomData<R>,
6681}
6682
6683impl<R> PreparedCall<R> {
6684 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6691 store
6692 .concurrent_state_mut()?
6693 .increment_task_ref_count(self.thread.task)?;
6694
6695 Ok(TaskId {
6696 task: self.thread.task,
6697 runtime_instance: self.runtime_instance,
6698 })
6699 }
6700}
6701
6702pub(crate) struct TaskId {
6704 task: TableId<GuestTask>,
6705 runtime_instance: RuntimeInstance,
6706}
6707
6708impl TaskId {
6709 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6717 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6718 if !task.already_lowered_parameters() {
6719 store.cancel_guest_subtask_without_lowered_parameters(
6720 self.runtime_instance,
6721 self.task,
6722 )?;
6723 }
6724
6725 store
6726 .concurrent_state_mut()?
6727 .decrement_task_ref_count(self.task)?;
6728
6729 Ok(())
6730 }
6731}
6732
6733pub(crate) fn prepare_call<T, R>(
6739 mut store: StoreContextMut<T>,
6740 handle: Func,
6741 param_count: usize,
6742 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6743 + Send
6744 + Sync
6745 + 'static,
6746 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6747 + Send
6748 + Sync
6749 + 'static,
6750) -> Result<PreparedCall<R>> {
6751 if !store.0.may_enter() {
6752 bail!(Trap::CannotEnterComponent);
6753 }
6754
6755 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6756
6757 let instance = handle.instance().id().get(store.0);
6758 let options = &instance.component().env_component().options[options];
6759 let ty = &instance.component().types()[ty];
6760 let async_typed = ty.async_;
6761 let async_lifted = raw_options.async_;
6762 let task_return_type = ty.results;
6763 let component_instance = raw_options.instance;
6764 let callback = options.callback.map(|i| instance.runtime_callback(i));
6765 let memory = options
6766 .memory()
6767 .map(|i| instance.runtime_memory(i))
6768 .map(SendSyncPtr::new);
6769 let string_encoding = options.string_encoding;
6770 let token = StoreToken::new(store.as_context_mut());
6771 let caller = store.0.materialize_host_task_id()?;
6772 let state = store.0.concurrent_state_mut()?;
6773
6774 let (tx, rx) = oneshot::channel();
6775
6776 let instance = handle.instance().runtime_instance(component_instance);
6777 let thread = GuestTask::new(
6778 state,
6779 Box::new(for_any_lower(move |store, params| {
6780 lower_params(token.as_context_mut(store), params)
6781 })),
6782 LiftResult {
6783 lift: Box::new(for_any_lift(move |store, result| {
6784 lift_result(store, result)
6785 })),
6786 ty: task_return_type,
6787 memory,
6788 string_encoding,
6789 },
6790 Caller::Host {
6791 tx: Some(tx),
6792 caller,
6793 },
6794 callback.map(|callback| {
6795 let callback = SendSyncPtr::new(callback);
6796 let instance = handle.instance();
6797 Box::new(move |store: &mut dyn VMStore, event, handle| {
6798 let store = token.as_context_mut(store);
6799 unsafe { instance.call_callback(store, callback, event, handle) }
6802 }) as CallbackFn
6803 }),
6804 instance,
6805 async_typed,
6806 async_lifted,
6807 )?;
6808
6809 Ok(PreparedCall {
6810 handle,
6811 thread,
6812 param_count,
6813 runtime_instance: instance,
6814 rx,
6815 _phantom: PhantomData,
6816 })
6817}
6818
6819pub(crate) struct StagedCall<R> {
6820 store: StoreId,
6821 rx: oneshot::Receiver<LiftedResult>,
6822 _marker: PhantomData<fn() -> R>,
6823 group: TaskGroupId,
6831}
6832
6833impl<R> StagedCall<R> {
6834 pub(crate) fn new<T: 'static>(
6845 mut store: StoreContextMut<T>,
6846 prepared: PreparedCall<R>,
6847 ) -> Result<StagedCall<R>> {
6848 let PreparedCall {
6849 handle,
6850 thread,
6851 param_count,
6852 rx,
6853 ..
6854 } = prepared;
6855
6856 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6857
6858 let state = store.0.concurrent_state_mut()?;
6859 let group = state.get_mut(thread.task)?.group;
6860 state.decrement_task_ref_count(thread.task)?;
6861
6862 Ok(StagedCall {
6863 store: store.0.id(),
6864 rx,
6865 _marker: PhantomData,
6866 group,
6867 })
6868 }
6869}
6870
6871impl<R> Future for StagedCall<R>
6872where
6873 R: 'static,
6874{
6875 type Output = Result<R>;
6876
6877 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6878 check_ambient_store(self.store);
6879 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6880 Ok(r) => match r.downcast() {
6881 Ok(r) => Ok(*r),
6882 Err(_) => bail_bug!("wrong type of value produced"),
6883 },
6884 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6885 })
6886 }
6887}
6888
6889fn stage_call0<T: 'static>(
6892 store: StoreContextMut<T>,
6893 handle: Func,
6894 guest_thread: QualifiedThreadId,
6895 param_count: usize,
6896) -> Result<()> {
6897 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6898 let is_concurrent = raw_options.async_;
6899 let callback = raw_options.callback;
6900 let instance = handle.instance();
6901 let callee = handle.lifted_core_func(store.0);
6902 let post_return = raw_options
6903 .post_return
6904 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6905 let callback = callback.map(|i| {
6906 let instance = instance.id().get(store.0);
6907 SendSyncPtr::new(instance.runtime_callback(i))
6908 });
6909
6910 log::trace!("queueing call {guest_thread:?}");
6911
6912 unsafe {
6916 instance.stage_call(
6917 store,
6918 guest_thread,
6919 SendSyncPtr::new(callee),
6920 param_count,
6921 1,
6922 is_concurrent,
6923 callback,
6924 post_return.map(SendSyncPtr::new),
6925 true,
6926 )
6927 }
6928}
6929
6930#[cfg(all(test, feature = "cranelift", feature = "wat"))]
6931mod tests {
6932 use super::*;
6933 use crate::component::{Component, Linker};
6934 use crate::store::AsStoreOpaque;
6935 use crate::{Config, Engine};
6936
6937 fn host_subtask(
6938 state: HostTaskState,
6939 event: Option<Event>,
6940 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
6941 let mut config = Config::new();
6942 config.wasm_component_model_async(true);
6943 let engine = Engine::new(&config)?;
6944 let component = Component::new(&engine, "(component)")?;
6945 let mut store = Store::new(&engine, ());
6946 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
6947 let store_opaque = store.as_store_opaque();
6948 let concurrent_state = store_opaque.concurrent_state_mut()?;
6949 let group = concurrent_state.make_task_group()?;
6952 let task = concurrent_state.push(HostTask {
6953 common: WaitableCommon::default(),
6954 call_context: CallContext::default(),
6955 state,
6956 group,
6957 })?;
6958 let handle = store_opaque
6959 .instance_state(instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)))
6960 .handle_table()
6961 .subtask_insert_host(task.rep())?;
6962 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
6963 common.handle = Some(handle);
6964 common.event = event;
6965 Ok((store, instance, task, handle))
6966 }
6967
6968 #[test]
6969 fn host_subtask_drop_during_cancellation() -> Result<()> {
6970 for abort_completed in [false, true] {
6971 let (handle, future) = JoinHandle::run(future::pending::<()>());
6972 let mut future = pin!(future);
6973 let (mut store, instance, task, handle) =
6974 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
6975 let store = store.as_store_opaque();
6976 let caller = RuntimeComponentInstanceIndex::from_u32(0);
6977 assert_eq!(
6978 instance.subtask_cancel(store, caller, true, handle)?,
6979 BLOCKED
6980 );
6981 if abort_completed {
6982 assert!(matches!(
6985 future
6986 .as_mut()
6987 .poll(&mut Context::from_waker(Waker::noop())),
6988 Poll::Ready(None),
6989 ));
6990 }
6991 for async_ in [false, true] {
6992 let err = instance
6993 .subtask_cancel(store, caller, async_, handle)
6994 .unwrap_err();
6995 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
6996 }
6997 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
6998 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
6999 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7000 }
7001 Ok(())
7002 }
7003
7004 #[test]
7005 fn host_subtask_cancel_after_completion() -> Result<()> {
7006 for async_ in [false, true] {
7007 let (mut store, instance, task, handle) = host_subtask(
7008 HostTaskState::CalleeDone { cancelled: false },
7009 Some(Event::Subtask {
7010 status: Status::Returned,
7011 }),
7012 )?;
7013 let store = store.as_store_opaque();
7014 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7015 assert_eq!(
7016 instance.subtask_cancel(store, caller, async_, handle)?,
7017 Status::Returned as u32,
7018 );
7019 let err = instance
7020 .subtask_cancel(store, caller, async_, handle)
7021 .unwrap_err();
7022 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7023 instance.subtask_drop(store, caller, handle)?;
7024 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7025 }
7026 Ok(())
7027 }
7028
7029 #[test]
7030 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7031 for (cancelled, status) in [
7032 (false, Status::Returned),
7033 (true, Status::Returned),
7034 (true, Status::ReturnCancelled),
7035 ] {
7036 for delivered in [false, true] {
7037 let event = if delivered {
7038 None
7039 } else {
7040 Some(Event::Subtask { status })
7041 };
7042 let (mut store, instance, task, handle) =
7043 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7044 let store = store.as_store_opaque();
7045 let result = instance.subtask_drop(
7046 store,
7047 RuntimeComponentInstanceIndex::from_u32(0),
7048 handle,
7049 );
7050 if delivered {
7051 result?;
7052 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7053 } else {
7054 let err = result.unwrap_err();
7055 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7056 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7057 }
7058 }
7059 }
7060 Ok(())
7061 }
7062}