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::{
71 AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, bail,
72};
73use crate::{Instance as ModuleInstance, bail_bug};
74use alloc::borrow::ToOwned;
75use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
76use core::any::Any;
77use core::cell::UnsafeCell;
78use core::fmt;
79use core::future;
80use core::future::Future;
81use core::marker::PhantomData;
82use core::mem::{self, ManuallyDrop, MaybeUninit};
83use core::ops::DerefMut;
84use core::pin::{Pin, pin};
85use core::ptr::{self, NonNull};
86use core::task::{Context, Poll, Waker};
87use futures::channel::oneshot;
88use futures::stream::{FuturesUnordered, StreamExt};
89use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
90use table::{TableDebug, TableId};
91use wasmtime_environ::component::{
92 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
93 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
94 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
95 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
96 TypeFuncIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
97};
98use wasmtime_environ::packed_option::ReservedValue;
99use wasmtime_environ::{NUM_COMPONENT_CONTEXT_SLOTS, Trap};
100#[cfg(feature = "gc")]
101use wasmtime_unwinder::Unwind;
102
103pub use abort::JoinHandle;
104pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
105pub use future_stream_any::{FutureAny, StreamAny};
106pub use futures_and_streams::{
107 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
108 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
109 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
110};
111pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
112#[cfg(feature = "task-group-hook")]
113pub use task_group_hook::TaskGroupHook;
114pub use task_group_hook::TaskGroupId;
115
116mod abort;
117mod error_contexts;
118mod func;
119mod future_stream_any;
120mod futures_and_streams;
121pub(crate) mod table;
122#[cfg(feature = "task-group-hook")]
123mod task_group_hook;
124#[cfg(not(feature = "task-group-hook"))]
125mod task_group_hook_disabled;
126#[cfg(not(feature = "task-group-hook"))]
127use task_group_hook_disabled as task_group_hook;
128pub(crate) mod tls;
129
130const BLOCKED: u32 = 0xffff_ffff;
133
134#[derive(Clone, Copy, Eq, PartialEq, Debug)]
136pub enum Status {
137 Starting = 0,
138 Started = 1,
139 Returned = 2,
140 StartCancelled = 3,
141 ReturnCancelled = 4,
142}
143
144impl Status {
145 pub fn pack(self, waitable: Option<u32>) -> u32 {
151 assert!(matches!(self, Status::Returned) == waitable.is_none());
152 let waitable = waitable.unwrap_or(0);
153 assert!(waitable < (1 << 28));
154 (waitable << 4) | (self as u32)
155 }
156}
157
158#[derive(Clone, Copy, Debug)]
161enum Event {
162 None,
163 Subtask {
164 status: Status,
165 },
166 StreamRead {
167 code: ReturnCode,
168 pending: Option<(TypeStreamTableIndex, u32)>,
169 },
170 StreamWrite {
171 code: ReturnCode,
172 pending: Option<(TypeStreamTableIndex, u32)>,
173 },
174 FutureRead {
175 code: ReturnCode,
176 pending: Option<(TypeFutureTableIndex, u32)>,
177 },
178 FutureWrite {
179 code: ReturnCode,
180 pending: Option<(TypeFutureTableIndex, u32)>,
181 },
182 Cancelled,
183}
184
185impl Event {
186 fn parts(self) -> (u32, u32) {
191 const EVENT_NONE: u32 = 0;
192 const EVENT_SUBTASK: u32 = 1;
193 const EVENT_STREAM_READ: u32 = 2;
194 const EVENT_STREAM_WRITE: u32 = 3;
195 const EVENT_FUTURE_READ: u32 = 4;
196 const EVENT_FUTURE_WRITE: u32 = 5;
197 const EVENT_CANCELLED: u32 = 6;
198 match self {
199 Event::None => (EVENT_NONE, 0),
200 Event::Cancelled => (EVENT_CANCELLED, 0),
201 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
202 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
203 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
204 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
205 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
206 }
207 }
208}
209
210mod callback_code {
212 pub const EXIT: u32 = 0;
213 pub const YIELD: u32 = 1;
214 pub const WAIT: u32 = 2;
215}
216
217const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
221
222pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
228 store: StoreContextMut<'a, T>,
229 get_data: fn(&mut T) -> D::Data<'_>,
230}
231
232impl<'a, T, D> Access<'a, T, D>
233where
234 D: HasData + ?Sized,
235 T: 'static,
236{
237 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
239 Self { store, get_data }
240 }
241
242 pub fn data_mut(&mut self) -> &mut T {
244 self.store.data_mut()
245 }
246
247 pub fn get(&mut self) -> D::Data<'_> {
249 (self.get_data)(self.data_mut())
250 }
251
252 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
256 where
257 T: 'static,
258 {
259 let accessor = Accessor {
260 get_data: self.get_data,
261 token: StoreToken::new(self.store.as_context_mut()),
262 };
263 self.store
264 .as_context_mut()
265 .spawn_with_accessor(accessor, task)
266 }
267
268 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
271 self.get_data
272 }
273}
274
275impl<'a, T, D> AsContext for Access<'a, T, D>
276where
277 D: HasData + ?Sized,
278 T: 'static,
279{
280 type Data = T;
281
282 fn as_context(&self) -> StoreContext<'_, T> {
283 self.store.as_context()
284 }
285}
286
287impl<'a, T, D> AsContextMut for Access<'a, T, D>
288where
289 D: HasData + ?Sized,
290 T: 'static,
291{
292 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
293 self.store.as_context_mut()
294 }
295}
296
297pub struct Accessor<T: 'static, D = HasSelf<T>>
357where
358 D: HasData + ?Sized,
359{
360 token: StoreToken<T>,
361 get_data: fn(&mut T) -> D::Data<'_>,
362}
363
364pub trait AsAccessor {
381 type Data: 'static;
383
384 type AccessorData: HasData + ?Sized;
387
388 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
390}
391
392impl<T: AsAccessor + ?Sized> AsAccessor for &T {
393 type Data = T::Data;
394 type AccessorData = T::AccessorData;
395
396 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
397 T::as_accessor(self)
398 }
399}
400
401impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
402 type Data = T;
403 type AccessorData = D;
404
405 fn as_accessor(&self) -> &Accessor<T, D> {
406 self
407 }
408}
409
410const _: () = {
433 const fn assert<T: Send + Sync>() {}
434 assert::<Accessor<UnsafeCell<u32>>>();
435};
436
437impl<T> Accessor<T> {
438 pub(crate) fn new(token: StoreToken<T>) -> Self {
447 Self {
448 token,
449 get_data: |x| x,
450 }
451 }
452}
453
454impl<T, D> Accessor<T, D>
455where
456 D: HasData + ?Sized,
457{
458 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
476 tls::get(|vmstore| {
477 fun(Access {
478 store: self.token.as_context_mut(vmstore),
479 get_data: self.get_data,
480 })
481 })
482 }
483
484 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
487 self.get_data
488 }
489
490 pub fn with_getter<D2: HasData>(
507 &self,
508 get_data: fn(&mut T) -> D2::Data<'_>,
509 ) -> Accessor<T, D2> {
510 Accessor {
511 token: self.token,
512 get_data,
513 }
514 }
515
516 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
532 where
533 T: 'static,
534 {
535 let accessor = self.clone_for_spawn();
536 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
537 }
538
539 fn clone_for_spawn(&self) -> Self {
540 Self {
541 token: self.token,
542 get_data: self.get_data,
543 }
544 }
545
546 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
582 self.with(|mut access| {
583 let store = access.as_context_mut().0;
584 let state = store.concurrent_state_mut_without_forcing_current_thread();
585 if state.interesting_tasks == 0 {
586 Poll::Ready(())
587 } else {
588 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
589 Poll::Pending
590 }
591 })
592 }
593
594 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
611 self.with(|mut access| {
612 let store = access.as_context_mut().0;
613 let (_, _, _, raw_options) = func.abi_info(store);
614 let instance = func.instance().runtime_instance(raw_options.instance);
615 let state = store.instance_state(instance).concurrent_state();
616 if state.backpressure == 0 {
617 Poll::Ready(())
618 } else {
619 store
620 .concurrent_state_mut_without_forcing_current_thread()
621 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
622 Poll::Pending
623 }
624 })
625 }
626}
627
628pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
650 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
651where
652 D: HasData + ?Sized,
653{
654 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
656}
657
658impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
659where
660 T: 'static,
661 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
662 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
663 Fut: Future<Output = Result<()>> + Send + 'fut,
664 D: HasData,
665{
666 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
667 (self)(accessor)
668 }
669}
670
671enum CallerInfo {
674 Async {
676 params: Vec<ValRaw>,
677 has_result: bool,
678 },
679 Sync {
681 params: Vec<ValRaw>,
682 result_count: u32,
683 },
684}
685
686enum WaitMode {
688 Fiber(StoreFiber<'static>),
690 Callback(Instance),
693}
694
695impl fmt::Debug for WaitMode {
696 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697 match self {
698 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
699 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
700 }
701 }
702}
703
704#[derive(Debug)]
706enum SuspendReason {
707 Waiting {
710 set: TableId<WaitableSet>,
711 thread: QualifiedThreadId,
712 },
713 YieldingToSubtask { thread: QualifiedThreadId },
716 NeedWork,
719 Yielding { thread: QualifiedThreadId },
722 ExplicitlySuspending { thread: QualifiedThreadId },
725}
726
727enum GuestCallKind {
729 DeliverEvent {
732 instance: Instance,
734 set: Option<TableId<WaitableSet>>,
739 },
740 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
746 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
747}
748
749impl fmt::Debug for GuestCallKind {
750 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
751 match self {
752 Self::DeliverEvent { instance, set } => f
753 .debug_struct("DeliverEvent")
754 .field("instance", instance)
755 .field("set", set)
756 .finish(),
757 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
758 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
759 }
760 }
761}
762
763#[derive(Copy, Clone, Debug)]
765pub enum SuspensionTarget {
766 Resume(u32),
767 Promote(u32),
768 None,
769}
770
771#[derive(Copy, Clone, Debug)]
773pub enum ResumeThread {
774 Promote,
775 Resume,
776 ResumeLater,
777}
778
779#[derive(Debug)]
781struct GuestCall {
782 thread: QualifiedThreadId,
783 kind: GuestCallKind,
784}
785
786impl GuestCall {
787 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
797 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
798 let async_typed = task.async_typed;
799 let instance = task.instance;
800 let state = store.instance_state(instance).concurrent_state();
801
802 let ready = match &self.kind {
803 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
804 GuestCallKind::StartImplicit(_) => {
805 !async_typed || !(state.do_not_enter || state.backpressure > 0)
806 }
807 GuestCallKind::StartExplicit(_) => true,
808 };
809 log::trace!(
810 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
811 state.do_not_enter,
812 state.backpressure
813 );
814 Ok(ready)
815 }
816}
817
818enum WorkerItem {
820 GuestCall(GuestCall),
821 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
822}
823
824enum WorkItem {
827 PushFuture(AlwaysMut<HostTaskFuture>),
829 ResumeFiber {
831 instance: RuntimeInstance,
832 thread: QualifiedThreadId,
833 fiber: StoreFiber<'static>,
834 },
835 ResumeThread {
837 instance: RuntimeInstance,
838 thread: QualifiedThreadId,
839 },
840 GuestCall {
842 instance: RuntimeInstance,
843 call: GuestCall,
844 },
845 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
847}
848
849impl fmt::Debug for WorkItem {
850 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
851 match self {
852 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
853 Self::ResumeFiber {
854 instance, thread, ..
855 } => f
856 .debug_struct("ResumeFiber")
857 .field("instance", instance)
858 .field("thread", thread)
859 .finish(),
860 Self::ResumeThread { instance, thread } => f
861 .debug_struct("ResumeThread")
862 .field("instance", instance)
863 .field("thread", thread)
864 .finish(),
865 Self::GuestCall { instance, call } => f
866 .debug_struct("GuestCall")
867 .field("instance", instance)
868 .field("call", call)
869 .finish(),
870 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
871 }
872 }
873}
874
875#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
877pub(crate) enum WaitResult {
878 Cancelled,
879 Completed,
880}
881
882pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
890 store: &mut dyn VMStore,
891 host_task: EnteredHostTask,
892 future: impl Future<Output = Result<R>> + Send + 'static,
893) -> Result<R> {
894 let mut future = Box::pin(future);
901 let poll = tls::set(store, || {
902 future
903 .as_mut()
904 .poll(&mut Context::from_waker(&Waker::noop()))
905 });
906
907 let caller = match host_task {
908 Some(caller) => caller,
909 None => bail_bug!("host task wasn't created but should have been"),
910 };
911
912 let task = match poll {
913 Poll::Ready(result) => return result,
915
916 Poll::Pending => {
921 let Some(task) = store.materialize_host_task_id()? else {
922 bail_bug!("current thread is not a host thread")
923 };
924
925 let future = Box::pin(async move {
928 let result = run_with_host_task_set(task, future).await??;
929 tls::get(move |store| {
930 let state = store.concurrent_state_mut()?;
931 let host_state = &mut state.get_mut(task)?.state;
932 assert!(matches!(host_state, HostTaskState::CalleeStarted));
933 *host_state = HostTaskState::CalleeFinished(Box::new(result));
934
935 Waitable::Host(task).set_event(
936 state,
937 Some(Event::Subtask {
938 status: Status::Returned,
939 }),
940 )?;
941
942 Ok(())
943 })
944 }) as HostTaskFuture;
945
946 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
947 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
948
949 let state = store.concurrent_state_mut()?;
950 state.push_future(future);
951
952 let set = state.get_mut(caller.thread)?.sync_call_set;
953 Waitable::Host(task).join(state, Some(set))?;
954
955 store.suspend(SuspendReason::Waiting {
956 set,
957 thread: caller,
958 })?;
959
960 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
964 task
965 }
966 };
967
968 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
970 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
971 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
972 Ok(result) => *result,
973 Err(_) => bail_bug!("host task finished with wrong type of result"),
974 }),
975 _ => bail_bug!("unexpected host task state after completion"),
976 }
977}
978
979fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
981 match call.kind {
982 GuestCallKind::DeliverEvent { instance, set } => {
983 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
984 Some(pair) => pair,
985 None => bail_bug!("delivering non-present event"),
986 };
987 let state = store.concurrent_state_mut()?;
988 let task = state.get_mut(call.thread.task)?;
989 let runtime_instance = task.instance;
990 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
991
992 log::trace!(
993 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
994 call.thread,
995 );
996
997 let old_thread = store.set_thread(call.thread)?;
998 log::trace!(
999 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
1000 call.thread
1001 );
1002
1003 store.enter_instance(runtime_instance);
1004
1005 let Some(callback) = store
1006 .concurrent_state_mut()?
1007 .get_mut(call.thread.task)?
1008 .callback
1009 .take()
1010 else {
1011 bail_bug!("guest task callback field not present")
1012 };
1013
1014 let code = callback(store, event, handle)?;
1015
1016 store
1017 .concurrent_state_mut()?
1018 .get_mut(call.thread.task)?
1019 .callback = Some(callback);
1020
1021 store.exit_instance(runtime_instance)?;
1022
1023 store.set_thread(old_thread)?;
1024
1025 instance.handle_callback_code(store, call.thread, runtime_instance.index, code)?;
1026
1027 log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
1028 }
1029 GuestCallKind::StartImplicit(fun) => {
1030 fun(store)?;
1031 }
1032 GuestCallKind::StartExplicit(fun) => {
1033 fun(store)?;
1034 }
1035 }
1036
1037 Ok(())
1038}
1039
1040impl<T> Store<T> {
1041 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1043 where
1044 T: Send + 'static,
1045 {
1046 ensure!(
1047 self.as_context().0.concurrency_support(),
1048 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1049 );
1050 self.as_context_mut().run_concurrent(fun).await
1051 }
1052
1053 #[doc(hidden)]
1054 pub fn assert_concurrent_state_empty(&mut self) {
1055 self.as_context_mut().assert_concurrent_state_empty();
1056 }
1057
1058 #[doc(hidden)]
1059 pub fn concurrent_state_table_size(&mut self) -> usize {
1060 self.as_context_mut().concurrent_state_table_size()
1061 }
1062
1063 pub fn spawn(
1065 &mut self,
1066 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1067 ) -> Result<JoinHandle>
1068 where
1069 T: 'static,
1070 {
1071 self.as_context_mut().spawn(task)
1072 }
1073}
1074
1075impl<T> StoreContextMut<'_, T> {
1076 #[doc(hidden)]
1087 pub fn assert_concurrent_state_empty(self) {
1088 let store = self.0;
1089 store
1090 .store_data_mut()
1091 .components
1092 .assert_instance_states_empty();
1093 let state = store.concurrent_state_mut().unwrap();
1094 assert!(
1095 state.table.get_mut().is_empty(),
1096 "non-empty table: {:?}",
1097 state.table.get_mut()
1098 );
1099 assert!(state.switch_item.is_none());
1100 assert!(state.next_switch_item.is_none());
1101 assert!(state.high_priority.is_empty());
1102 assert!(state.low_priority.is_empty());
1103 assert!(state.unforced_current_thread.is_none());
1104 assert!(state.deferred_host_call_context.is_none());
1105 assert!(state.futures_mut().unwrap().is_empty());
1106 assert!(state.global_error_context_ref_counts.is_empty());
1107 }
1108
1109 #[doc(hidden)]
1114 pub fn concurrent_state_table_size(&mut self) -> usize {
1115 self.0
1116 .concurrent_state_mut()
1117 .unwrap()
1118 .table
1119 .get_mut()
1120 .iter_mut()
1121 .count()
1122 }
1123
1124 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1134 where
1135 T: 'static,
1136 {
1137 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1138 self.spawn_with_accessor(accessor, task)
1139 }
1140
1141 fn spawn_with_accessor<D>(
1144 self,
1145 accessor: Accessor<T, D>,
1146 task: impl for<'fut> AccessorTask<'fut, T, D>,
1147 ) -> Result<JoinHandle>
1148 where
1149 T: 'static,
1150 D: HasData + ?Sized,
1151 {
1152 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1156 self.0
1157 .concurrent_state_mut()?
1158 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1159 Ok(handle)
1160 }
1161
1162 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1246 where
1247 T: Send + 'static,
1248 {
1249 ensure!(
1250 self.0.concurrency_support(),
1251 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1252 );
1253 self.do_run_concurrent(fun, false).await
1254 }
1255
1256 pub(super) async fn run_concurrent_trap_on_idle<R>(
1257 self,
1258 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1259 ) -> Result<R> {
1260 self.do_run_concurrent(fun, true).await
1261 }
1262
1263 async fn do_run_concurrent<R>(
1264 mut self,
1265 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1266 trap_on_idle: bool,
1267 ) -> Result<R> {
1268 debug_assert!(self.0.concurrency_support());
1269 let already_running = self
1270 .0
1271 .concurrent_state_mut_already_forced_current_thread()
1272 .event_loop_running;
1273 if already_running {
1274 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1275 }
1276 let token = StoreToken::new(self.as_context_mut());
1277
1278 struct Dropper<'a, T: 'static, V> {
1279 store: StoreContextMut<'a, T>,
1280 value: ManuallyDrop<V>,
1281 }
1282
1283 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1284 fn drop(&mut self) {
1285 self.store
1286 .0
1287 .concurrent_state_mut_already_forced_current_thread()
1288 .event_loop_running = false;
1289
1290 tls::set(self.store.0, || {
1291 unsafe { ManuallyDrop::drop(&mut self.value) }
1296 });
1297 }
1298 }
1299
1300 let accessor = &Accessor::new(token);
1301 self.0
1302 .concurrent_state_mut_already_forced_current_thread()
1303 .event_loop_running = true;
1304 let dropper = &mut Dropper {
1305 store: self,
1306 value: ManuallyDrop::new(fun(accessor)),
1307 };
1308 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1310
1311 let result = dropper
1312 .store
1313 .as_context_mut()
1314 .poll_until(future, trap_on_idle)
1315 .await;
1316
1317 if result.is_err() {
1318 dropper.store.0.set_trapped();
1319 }
1320
1321 result
1322 }
1323
1324 async fn poll_until<R>(
1330 mut self,
1331 mut future: Pin<&mut impl Future<Output = R>>,
1332 trap_on_idle: bool,
1333 ) -> Result<R> {
1334 struct Reset<'a, T: 'static> {
1335 store: StoreContextMut<'a, T>,
1336 futures: Option<FuturesUnordered<HostTaskFuture>>,
1337 }
1338
1339 impl<'a, T> Drop for Reset<'a, T> {
1340 fn drop(&mut self) {
1341 if let Some(futures) = self.futures.take() {
1342 *self
1343 .store
1344 .0
1345 .concurrent_state_mut_already_forced_current_thread()
1346 .futures
1347 .get_mut() = Some(futures);
1348 }
1349 }
1350 }
1351
1352 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1356 let mut turns_without_yield = 0;
1357
1358 loop {
1359 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1363 let mut reset = Reset {
1364 store: self.as_context_mut(),
1365 futures,
1366 };
1367 let mut next = match reset.futures.as_mut() {
1368 Some(f) => pin!(f.next()),
1369 None => bail_bug!("concurrent state missing futures field"),
1370 };
1371
1372 enum PollResult<R> {
1373 Complete(R),
1374 ProcessWork {
1375 ready: Option<WorkItem>,
1376 low_priority: bool,
1377 },
1378 }
1379
1380 let result = future::poll_fn(|cx| {
1381 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1384 return Poll::Ready(Ok(PollResult::Complete(value)));
1385 }
1386
1387 if reset.store.0.trapped() {
1396 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1397 }
1398
1399 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1403 Poll::Ready(Some(output)) => {
1404 match output {
1405 Err(e) => return Poll::Ready(Err(e)),
1406 Ok(()) => {}
1407 }
1408 Poll::Ready(true)
1409 }
1410 Poll::Ready(None) => Poll::Ready(false),
1411 Poll::Pending => Poll::Pending,
1412 };
1413
1414 let state = reset.store.0.concurrent_state_mut()?;
1429 let mut ready = state.switch_item.take();
1430 let mut low_priority = false;
1431 if ready.is_none() {
1432 ready = state.high_priority.pop_back();
1433 if ready.is_none() {
1434 ready = state.low_priority.pop_back();
1435 low_priority = true;
1436 }
1437 }
1438 if ready.is_some() {
1439 return Poll::Ready(Ok(PollResult::ProcessWork {
1440 ready,
1441 low_priority,
1442 }));
1443 }
1444
1445 return match next {
1449 Poll::Ready(true) => {
1450 Poll::Ready(Ok(PollResult::ProcessWork {
1456 ready: None,
1457 low_priority: false,
1458 }))
1459 }
1460 Poll::Ready(false) => {
1461 if let Poll::Ready(value) =
1465 tls::set(reset.store.0, || future.as_mut().poll(cx))
1466 {
1467 Poll::Ready(Ok(PollResult::Complete(value)))
1468 } else {
1469 if trap_on_idle {
1475 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1482 Trap::CannotBlockSyncTask.into()
1483 } else {
1484 Trap::AsyncDeadlock.into()
1486 }))
1487 } else {
1488 Poll::Pending
1492 }
1493 }
1494 }
1495 Poll::Pending => Poll::Pending,
1500 };
1501 })
1502 .await;
1503
1504 drop(reset);
1508
1509 match result? {
1510 PollResult::Complete(value) => break Ok(value),
1513 PollResult::ProcessWork {
1516 ready,
1517 low_priority,
1518 } => {
1519 struct Dispose<'a, T: 'static> {
1520 store: StoreContextMut<'a, T>,
1521 ready: Option<WorkItem>,
1522 }
1523
1524 impl<'a, T> Drop for Dispose<'a, T> {
1525 fn drop(&mut self) {
1526 if let Some(item) = self.ready.take() {
1527 match item {
1528 WorkItem::ResumeFiber { mut fiber, .. } => {
1529 fiber.dispose(self.store.0)
1530 }
1531 WorkItem::PushFuture(future) => {
1532 tls::set(self.store.0, move || drop(future))
1533 }
1534 _ => {}
1535 }
1536 }
1537 }
1538 }
1539
1540 let mut dispose = Dispose {
1541 store: self.as_context_mut(),
1542 ready,
1543 };
1544
1545 if low_priority {
1567 dispose.store.0.yield_now().await;
1568 turns_without_yield = 0;
1569 }
1570
1571 if let Some(item) = dispose.ready.take() {
1572 dispose
1573 .store
1574 .as_context_mut()
1575 .handle_work_item(item)
1576 .await?;
1577 }
1578
1579 turns_without_yield += 1;
1580 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1581 turns_without_yield = 0;
1582 dispose.store.0.yield_now().await;
1583 }
1584 }
1585 }
1586 }
1587 }
1588
1589 async fn handle_work_item(self, item: WorkItem) -> Result<()> {
1591 log::trace!("handle work item {item:?}");
1592 match item {
1593 WorkItem::PushFuture(future) => {
1594 self.0
1595 .concurrent_state_mut()?
1596 .futures_mut()?
1597 .push(future.into_inner());
1598 }
1599 WorkItem::ResumeFiber { fiber, .. } => {
1600 self.0.resume_fiber(fiber).await?;
1601 }
1602 WorkItem::ResumeThread { thread, .. } => {
1603 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1604 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1605 GuestThreadState::Running,
1606 ) {
1607 self.0.resume_fiber(fiber).await?;
1608 } else {
1609 bail_bug!("cannot resume non-pending thread {thread:?}");
1610 }
1611 }
1612 WorkItem::GuestCall { call, .. } => {
1613 if call.is_ready(self.0)? {
1614 self.0
1615 .concurrent_state_mut()?
1616 .get_mut(call.thread.thread)?
1617 .wake_on_cancel = WakeOnCancel::None;
1618 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1619 } else {
1620 let state = self.0.concurrent_state_mut()?;
1621 let task = state.get_mut(call.thread.task)?;
1622 if !task.starting_sent {
1623 task.starting_sent = true;
1624 if let GuestCallKind::StartImplicit(_) = &call.kind {
1625 Waitable::Guest(call.thread.task).set_event(
1626 state,
1627 Some(Event::Subtask {
1628 status: Status::Starting,
1629 }),
1630 )?;
1631 }
1632 }
1633
1634 let instance = state.get_mut(call.thread.task)?.instance;
1635 self.0
1636 .instance_state(instance)
1637 .concurrent_state()
1638 .pending
1639 .insert(call.thread, call.kind);
1640
1641 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1645 }
1646 }
1647 WorkItem::WorkerFunction(fun) => {
1648 self.run_on_worker(WorkerItem::Function(fun)).await?;
1649 }
1650 }
1651
1652 Ok(())
1653 }
1654
1655 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1657 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1658 fiber
1659 } else {
1660 unsafe {
1679 fiber::make_fiber_unchecked(self.0, move |store| {
1680 loop {
1681 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1682 bail_bug!("worker_item not present when resuming fiber")
1683 };
1684 match item {
1685 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1686 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1687 }
1688
1689 store.suspend(SuspendReason::NeedWork)?;
1690 }
1691 })?
1692 }
1693 };
1694
1695 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1696 assert!(worker_item.is_none());
1697 *worker_item = Some(item);
1698
1699 self.0.resume_fiber(worker).await
1700 }
1701
1702 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1707 where
1708 T: 'static,
1709 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1710 + Send
1711 + Sync
1712 + 'static,
1713 R: Send + Sync + 'static,
1714 {
1715 let token = StoreToken::new(self);
1716 async move {
1717 let mut accessor = Accessor::new(token);
1718 closure(&mut accessor).await
1719 }
1720 }
1721
1722 pub(crate) async fn start_instance(
1723 &mut self,
1724 instance: ModuleInstance,
1725 ) -> Result<ModuleInstance> {
1726 let (tx, rx) = oneshot::channel();
1727 let token = StoreToken::new(self.as_context_mut());
1728 self.0.queue_task(move |store| {
1729 _ = tx.send(
1730 instance
1731 .start_raw(&mut token.as_context_mut(store))
1732 .map(|()| instance),
1733 );
1734 Ok(())
1735 })?;
1736 self.as_context_mut()
1737 .run_concurrent_trap_on_idle(async |_| {
1738 rx.await
1739 .map_err(|_| format_err!("oneshot channel canceled"))
1740 })
1741 .await??
1742 }
1743}
1744
1745pub type EnteredHostTask = Option<QualifiedThreadId>;
1752
1753impl StoreOpaque {
1754 #[inline]
1758 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1759 if !self.concurrency_support() {
1761 return Ok(CurrentThread::None);
1762 }
1763
1764 if !self
1767 .vm_store_context_mut()
1768 .current_thread_mut()
1769 .is_deferred()
1770 {
1771 return Ok(self
1772 .concurrent_state_mut_already_forced_current_thread()
1773 .unforced_current_thread);
1774 }
1775
1776 self.force_deferred_current_thread()
1777 }
1778
1779 #[cold]
1782 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1783 let state = self.concurrent_state_mut_without_forcing_current_thread();
1792 let id = match state.unforced_current_thread.guest_task() {
1793 Some(task) => state.get_mut(task)?.instance.instance,
1794 None => bail_bug!("deferred component-model thread with non-guest base"),
1795 };
1796
1797 let mut frames = Vec::new();
1800 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1801 while let Some(ptr) = cur.as_deferred() {
1802 let deferred = unsafe { ptr.as_non_null().as_ref() };
1807 frames.push((
1808 deferred.callee_async != 0,
1809 deferred.callee_instance,
1810 deferred.saved_context,
1811 ));
1812 cur = deferred.parent;
1813 }
1814
1815 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1819
1820 let current_context = *self.vm_store_context_mut().component_context_mut();
1823
1824 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1828 *self.vm_store_context_mut().component_context_mut() = saved_context;
1832 let callee = RuntimeInstance {
1833 instance: id,
1834 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1835 };
1836 self.enter_guest_sync_call(callee_async, callee)?;
1837 }
1838
1839 *self.vm_store_context_mut().component_context_mut() = current_context;
1841
1842 Ok(self
1843 .concurrent_state_mut_without_forcing_current_thread()
1844 .unforced_current_thread)
1845 }
1846
1847 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1848 match self.current_thread()?.guest() {
1849 Some(id) => Ok(*id),
1850 None => bail_bug!("current thread is not a guest thread"),
1851 }
1852 }
1853
1854 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1858 match self.current_thread()? {
1859 CurrentThread::Host(id) => Ok(Some(id)),
1860 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1861 _ => bail_bug!("current thread is not a host thread"),
1862 }
1863 }
1864
1865 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1868 Ok(self
1869 .concurrent_state_mut()?
1870 .materialize_current_host_task_id()?)
1871 }
1872
1873 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1874 log::trace!("enter sync-typed call {callee:?}");
1875 let state = self.instance_state(callee).concurrent_state();
1876 let old_do_not_suspend = state.do_not_suspend;
1877 state.do_not_suspend = true;
1878
1879 let thread = self.current_guest_thread()?;
1880 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1881 if thread.old_do_not_suspend.is_some() {
1882 bail_bug!("current thread already has `old_do_not_suspend` value");
1883 }
1884
1885 thread.old_do_not_suspend = Some(old_do_not_suspend);
1886
1887 Ok(())
1888 }
1889
1890 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1891 log::trace!("exit sync-typed call {callee:?}");
1892 let thread = self.current_guest_thread()?;
1893 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1894 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1895 bail_bug!("current thread missing `old_do_not_suspend` value");
1896 };
1897 let state = self.instance_state(callee).concurrent_state();
1898 state.do_not_suspend = old_do_not_suspend;
1899 Ok(())
1900 }
1901
1902 pub(crate) fn enter_guest_sync_call(
1914 &mut self,
1915 callee_async_typed: bool,
1916 callee: RuntimeInstance,
1917 ) -> Result<()> {
1918 log::trace!("enter sync-lifted call {callee:?}");
1919 if !self.concurrency_support() {
1920 return self.enter_call_not_concurrent();
1921 }
1922
1923 let thread = self.current_thread()?;
1924 let caller = if let Some(thread) = thread.guest() {
1925 Caller::Guest { thread: *thread }
1926 } else {
1927 Caller::Host {
1928 tx: None,
1929 host_future_present: false,
1930 caller: self.materialize_host_task_id()?,
1931 }
1932 };
1933
1934 let state = self.concurrent_state_mut()?;
1935 let guest_thread = GuestTask::new(
1936 state,
1937 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1938 LiftResult {
1939 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1940 ty: TypeTupleIndex::reserved_value(),
1941 memory: None,
1942 string_encoding: StringEncoding::Utf8,
1943 },
1944 caller,
1945 None,
1946 callee,
1947 callee_async_typed,
1948 true,
1949 )?;
1950
1951 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1952 guest_thread.thread,
1953 self,
1954 callee.index,
1955 )?;
1956 self.set_thread(guest_thread)?;
1957
1958 if !callee_async_typed {
1959 self.enter_sync_call(callee)?;
1960 }
1961
1962 Ok(())
1963 }
1964
1965 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1973 if !self.concurrency_support() {
1974 return Ok(self.exit_call_not_concurrent());
1975 }
1976
1977 let thread = match self.current_thread()?.guest() {
1978 Some(t) => *t,
1979 None => bail_bug!("expected task when exiting"),
1980 };
1981 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1982 let instance = task.instance;
1983
1984 let caller = match &task.caller {
1985 &Caller::Guest { thread } => thread.into(),
1986 &Caller::Host { caller, .. } => caller
1987 .map(CurrentThread::Host)
1988 .unwrap_or(CurrentThread::None),
1989 };
1990 task.lift_result = None;
1991 task.exited = true;
1992 let async_typed = task.async_typed;
1993
1994 if !async_typed {
1995 self.exit_sync_call(instance)?;
1996 }
1997
1998 self.set_thread(caller)?;
1999
2000 log::trace!("exit sync-lifted call {instance:?}");
2001
2002 if async_typed {
2003 self.switch_or_trap_if_may_not_suspend(instance)?;
2008 }
2009
2010 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
2011
2012 Ok(())
2013 }
2014
2015 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2022 if !self.concurrency_support() {
2023 self.enter_call_not_concurrent()?;
2024 return Ok(None);
2025 }
2026 let caller = self.current_guest_thread()?;
2027 log::trace!("new deferred host task with caller {caller:?}");
2028
2029 self.set_thread(CurrentThread::DeferredHost(caller))?;
2030 let state = self.concurrent_state_mut()?;
2031 debug_assert!(state.deferred_host_call_context.is_none());
2032 state.deferred_host_call_context = Some(CallContext::default());
2033 state.debug_assert_deferred_host_invariant();
2034 Ok(Some(caller))
2035 }
2036
2037 pub(crate) fn host_task_delete(
2044 &mut self,
2045 original_task: EnteredHostTask,
2046 materialized_task: Option<TableId<HostTask>>,
2047 ) -> Result<()> {
2048 match original_task {
2049 Some(caller) => {
2050 self.set_thread(caller)?;
2051 if materialized_task.is_none() {
2052 let state = self.concurrent_state_mut()?;
2053 let context = state
2054 .deferred_host_call_context
2055 .take()
2056 .expect("deferred host call context should be present");
2057 debug_assert!(context.is_empty());
2058 state.debug_assert_deferred_host_invariant();
2059 }
2060 log::trace!(
2061 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2062 );
2063 if let Some(task) = materialized_task {
2064 Waitable::Host(task).delete_from(self)?;
2065 }
2066 }
2067 None => {
2068 debug_assert!(materialized_task.is_none());
2069 self.exit_call_not_concurrent();
2070 }
2071 }
2072 Ok(())
2073 }
2074
2075 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2078 self.component_instance_mut(instance.instance)
2079 .instance_state(instance.index)
2080 }
2081
2082 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2088 let thread = thread.into();
2089 let state = self.concurrent_state_mut()?;
2090 state.debug_assert_deferred_host_invariant();
2091 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2092
2093 state.handle_thread_switch(old_thread, thread)?;
2094
2095 if let Some(old_thread) = old_thread.guest() {
2103 let old_context = *self.vm_store_context_mut().component_context_mut();
2104 self.concurrent_state_mut()?
2105 .get_mut(old_thread.thread)?
2106 .context = old_context;
2107 }
2108 if cfg!(debug_assertions) {
2109 *self.vm_store_context_mut().component_context_mut() =
2110 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2111 }
2112 if let Some(thread) = thread.guest() {
2113 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2114 let context = thread.context;
2115 if cfg!(debug_assertions) {
2116 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2117 }
2118 *self.vm_store_context_mut().component_context_mut() = context;
2119 }
2120
2121 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2123 VMLazyThread::none()
2124 } else {
2125 VMLazyThread::forced()
2126 };
2127
2128 Ok(old_thread)
2129 }
2130
2131 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2133 if self.switch_if_may_not_suspend(instance)? {
2134 Ok(())
2135 } else {
2136 Err(Trap::CannotBlockSyncTask.into())
2137 }
2138 }
2139
2140 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2144 self.concurrent_state_mut()?;
2148
2149 Ok(!self.concurrency_support()
2150 || !self
2151 .instance_state(instance)
2152 .concurrent_state()
2153 .do_not_suspend
2154 || self
2155 .concurrent_state_mut()?
2156 .promote_instance_local_thread_work_item(instance)?)
2157 }
2158
2159 fn enter_instance(&mut self, instance: RuntimeInstance) {
2163 log::trace!("enter {instance:?}");
2164 self.instance_state(instance)
2165 .concurrent_state()
2166 .do_not_enter = true;
2167 }
2168
2169 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2173 log::trace!("exit {instance:?}");
2174 self.instance_state(instance)
2175 .concurrent_state()
2176 .do_not_enter = false;
2177 self.partition_pending(instance)
2178 }
2179
2180 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2188 for (thread, kind) in
2189 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2190 {
2191 let call = GuestCall { thread, kind };
2192 if call.is_ready(self)? {
2193 self.concurrent_state_mut()?
2194 .push_high_priority(WorkItem::GuestCall { instance, call });
2195 } else {
2196 self.instance_state(instance)
2197 .concurrent_state()
2198 .pending
2199 .insert(call.thread, call.kind);
2200 }
2201 }
2202
2203 if let Some(waker) = self
2204 .concurrent_state_mut()?
2205 .ready_for_concurrent_call_waker
2206 .take()
2207 {
2208 waker.wake();
2209 }
2210
2211 Ok(())
2212 }
2213
2214 pub(crate) fn backpressure_modify(
2216 &mut self,
2217 caller_instance: RuntimeInstance,
2218 modify: impl FnOnce(u16) -> Option<u16>,
2219 ) -> Result<()> {
2220 let state = self.instance_state(caller_instance).concurrent_state();
2221 let old = state.backpressure;
2222 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2223 state.backpressure = new;
2224
2225 if old > 0 && new == 0 {
2226 self.partition_pending(caller_instance)?;
2229 }
2230
2231 Ok(())
2232 }
2233
2234 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2237 let old_thread = self.current_thread()?;
2238 log::trace!("resume_fiber: save current thread {old_thread:?}");
2239
2240 let fiber = fiber::resolve_or_release(self, fiber).await?;
2241
2242 self.set_thread(old_thread)?;
2243
2244 let state = self.concurrent_state_mut()?;
2245
2246 if let Some(ot) = old_thread.guest() {
2247 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2248 }
2249 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2250
2251 if let Some(mut fiber) = fiber {
2252 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2253 let reason = match state.suspend_reason.take() {
2255 Some(r) => r,
2256 None => bail_bug!("suspend reason missing when resuming fiber"),
2257 };
2258 match reason {
2259 SuspendReason::NeedWork => {
2260 if state.worker.is_none() {
2261 state.worker = Some(fiber);
2262 } else {
2263 fiber.dispose(self);
2264 }
2265 }
2266 SuspendReason::Yielding { thread } => {
2267 state.get_mut(thread.thread)?.state = GuestThreadState::Ready { fiber };
2268 let instance = state.get_mut(thread.task)?.instance;
2269 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2270 }
2271 SuspendReason::ExplicitlySuspending { thread } => {
2272 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2273 }
2274 SuspendReason::Waiting { set, thread } => {
2275 let old = state
2276 .get_mut(set)?
2277 .waiting
2278 .insert(thread, WaitMode::Fiber(fiber));
2279 assert!(old.is_none());
2280 }
2281 SuspendReason::YieldingToSubtask { thread } => {
2282 let item = WorkItem::ResumeFiber {
2291 instance: state.get_mut(thread.task)?.instance,
2292 thread,
2293 fiber,
2294 };
2295
2296 if state.next_switch_item.replace(item).is_some() {
2297 bail_bug!(
2300 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2301 a thread wanted to wait on a subtask"
2302 );
2303 }
2304 }
2305 };
2306 } else {
2307 log::trace!("resume_fiber: fiber has exited");
2308 }
2309
2310 Ok(())
2311 }
2312
2313 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2319 log::trace!("suspend fiber: {reason:?}");
2320
2321 let state = self.concurrent_state_mut()?;
2322
2323 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2330 SuspendReason::Yielding { .. }
2331 | SuspendReason::Waiting { .. }
2332 | SuspendReason::ExplicitlySuspending { .. } => {
2333 if state.switch_item.is_none() {
2336 state.take_next_switch_item()?;
2337 }
2338
2339 (true, false)
2340 }
2341 SuspendReason::YieldingToSubtask { .. } => (true, true),
2342 SuspendReason::NeedWork => (false, false),
2343 };
2344
2345 let old_next_switch_item = if save_and_restore_next_switch_item {
2346 let item = state.next_switch_item.take();
2347 Some(state.push(item)?)
2351 } else {
2352 None
2353 };
2354
2355 let old_guest_thread = if save_and_restore_thread {
2356 self.current_thread()?
2357 } else {
2358 CurrentThread::None
2359 };
2360
2361 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2362 assert!(suspend_reason.is_none());
2363 *suspend_reason = Some(reason);
2364
2365 if !self.fiber_async_state_mut().can_block() {
2368 return Err(format_err!("future dropped"));
2369 }
2370
2371 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2372
2373 if save_and_restore_thread {
2374 self.set_thread(old_guest_thread)?;
2375 }
2376
2377 if let Some(item) = old_next_switch_item {
2378 let state = self.concurrent_state_mut()?;
2379 state.next_switch_item = state.delete(item)?;
2380 }
2381
2382 Ok(())
2383 }
2384
2385 fn wait_for_event(
2386 &mut self,
2387 caller_instance: RuntimeInstance,
2388 waitable: Waitable,
2389 ) -> Result<()> {
2390 let caller = self.current_guest_thread()?;
2391 let state = self.concurrent_state_mut()?;
2392
2393 waitable.trap_if_in_waitable_set(state)?;
2394
2395 let set = state.get_mut(caller.thread)?.sync_call_set;
2396 waitable.join(state, Some(set))?;
2397
2398 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2399
2400 self.suspend(SuspendReason::Waiting {
2401 set,
2402 thread: caller,
2403 })?;
2404 let state = self.concurrent_state_mut()?;
2405
2406 waitable.join(state, None)
2407 }
2408
2409 fn cleanup_thread(
2431 &mut self,
2432 guest_thread: QualifiedThreadId,
2433 runtime_instance: RuntimeInstance,
2434 cleanup_task: CleanupTask,
2435 ) -> Result<()> {
2436 let state = self.concurrent_state_mut()?;
2437 state.take_next_switch_item()?;
2440 let thread_data = state.get_mut(guest_thread.thread)?;
2441 let sync_call_set = thread_data.sync_call_set;
2442 if let Some(guest_id) = thread_data.instance_rep {
2443 self.instance_state(runtime_instance)
2444 .thread_handle_table()
2445 .guest_thread_remove(guest_id)?;
2446 }
2447 let state = self.concurrent_state_mut()?;
2448
2449 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2451 if let Some(Event::Subtask {
2452 status: Status::Returned | Status::ReturnCancelled,
2453 }) = waitable.common(self.concurrent_state_mut()?)?.event
2454 {
2455 waitable.delete_from(self)?;
2456 }
2457 }
2458
2459 let state = self.concurrent_state_mut()?;
2460 state.delete(guest_thread.thread)?;
2461 state.delete(sync_call_set)?;
2462 let task = state.get_mut(guest_thread.task)?;
2463 task.threads.remove(&guest_thread.thread);
2464
2465 if task.threads.is_empty() && !task.returned_or_cancelled() {
2466 bail!(Trap::NoAsyncResult);
2467 }
2468 let ready_to_delete = task.ready_to_delete();
2469
2470 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2471 task.decremented_interesting_task_count = true;
2472
2473 debug_assert!(state.interesting_tasks > 0);
2474 state.interesting_tasks -= 1;
2475 if state.interesting_tasks == 0
2476 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2477 {
2478 waker.wake();
2479 }
2480 }
2481
2482 match cleanup_task {
2483 CleanupTask::Yes => {
2484 if ready_to_delete {
2485 Waitable::Guest(guest_thread.task).delete_from(self)?;
2486 }
2487 }
2488 CleanupTask::No => {}
2489 }
2490
2491 Ok(())
2492 }
2493
2494 fn cancel_guest_subtask_without_lowered_parameters(
2507 &mut self,
2508 caller_instance: RuntimeInstance,
2509 guest_task: TableId<GuestTask>,
2510 ) -> Result<()> {
2511 let concurrent_state = self.concurrent_state_mut()?;
2512 let task = concurrent_state.get_mut(guest_task)?;
2513 assert!(!task.already_lowered_parameters());
2514 task.lower_params = None;
2518 task.lift_result = None;
2519 task.exited = true;
2520 let instance = task.instance;
2521
2522 assert_eq!(1, task.threads.len());
2525 let thread = *task.threads.iter().next().unwrap();
2526 self.cleanup_thread(
2527 QualifiedThreadId {
2528 task: guest_task,
2529 thread,
2530 },
2531 caller_instance,
2532 CleanupTask::No,
2533 )?;
2534
2535 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2537 let pending_count = pending.len();
2538 pending.retain(|thread, _| thread.task != guest_task);
2539 if pending.len() == pending_count {
2541 bail!(Trap::SubtaskCancelAfterTerminal);
2542 }
2543 Ok(())
2544 }
2545
2546 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2549 if !self.concurrency_support() {
2550 return Ok(self
2551 .current_scope_id_not_concurrent()?
2552 .map(|id| CurrentScope::Id(Scope::Id(id))));
2553 }
2554
2555 Ok(match self.current_thread()? {
2556 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2557 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2558 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2559 CurrentThread::None => return Ok(None),
2560 })
2561 }
2562
2563 pub(crate) fn queue_task(
2564 &mut self,
2565 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2566 ) -> Result<()> {
2567 self.concurrent_state_mut()?
2568 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2569 Ok(())
2570 }
2571
2572 fn any_may_not_suspend(&mut self) -> Result<bool> {
2581 Ok(self
2589 .concurrent_state_mut()?
2590 .table
2591 .get_mut()
2592 .iter_mut()
2593 .filter_map(|(_, entry)| {
2594 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2595 Some(task.instance)
2596 } else {
2597 None
2598 }
2599 })
2600 .collect::<Vec<_>>()
2601 .into_iter()
2602 .any(|instance| {
2603 self.instance_state(instance)
2604 .concurrent_state()
2605 .do_not_suspend
2606 }))
2607 }
2608}
2609
2610enum CleanupTask {
2611 Yes,
2612 No,
2613}
2614
2615impl Instance {
2616 fn get_event(
2619 self,
2620 store: &mut StoreOpaque,
2621 guest_task: TableId<GuestTask>,
2622 set: Option<TableId<WaitableSet>>,
2623 cancellable: bool,
2624 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2625 let state = store.concurrent_state_mut()?;
2626
2627 let task = state.get_mut(guest_task)?;
2628 let event = &mut task.event;
2629 if let Some(ev) = event
2630 && (cancellable || !matches!(ev, Event::Cancelled))
2631 {
2632 log::trace!("deliver event {ev:?} to {guest_task:?}");
2633
2634 if matches!(ev, Event::Cancelled) {
2635 task.cancel_request_delivered = true;
2636 }
2637
2638 let ev = *ev;
2639 *event = None;
2640 return Ok(Some((ev, None)));
2641 }
2642
2643 let set = match set {
2644 Some(set) => set,
2645 None => return Ok(None),
2646 };
2647 let waitable = match state.get_mut(set)?.ready.pop_first() {
2648 Some(v) => v,
2649 None => return Ok(None),
2650 };
2651
2652 let common = waitable.common(state)?;
2653 let handle = match common.handle {
2654 Some(h) => h,
2655 None => bail_bug!("handle not set when delivering event"),
2656 };
2657 let event = match common.event.take() {
2658 Some(e) => e,
2659 None => bail_bug!("event not set when delivering event"),
2660 };
2661
2662 log::trace!(
2663 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2664 );
2665
2666 waitable.on_delivery(store, self, event)?;
2667
2668 Ok(Some((event, Some((waitable, handle)))))
2669 }
2670
2671 fn handle_callback_code(
2677 self,
2678 store: &mut StoreOpaque,
2679 guest_thread: QualifiedThreadId,
2680 runtime_instance: RuntimeComponentInstanceIndex,
2681 code: u32,
2682 ) -> Result<()> {
2683 let (code, set) = unpack_callback_code(code);
2684
2685 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2686
2687 let state = store.concurrent_state_mut()?;
2688
2689 state.take_next_switch_item()?;
2690
2691 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2692 let set = store
2693 .instance_state(self.runtime_instance(runtime_instance))
2694 .handle_table()
2695 .waitable_set_rep(handle)?;
2696
2697 Ok(TableId::<WaitableSet>::new(set))
2698 };
2699
2700 match code {
2701 callback_code::EXIT => {
2702 log::trace!("implicit thread {guest_thread:?} completed");
2703 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2704 task.exited = true;
2705 task.callback = None;
2706
2707 let runtime_instance = self.runtime_instance(runtime_instance);
2708
2709 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2714
2715 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2716 }
2717 callback_code::YIELD => {
2718 let old = state
2721 .get_mut(guest_thread.thread)?
2722 .wake_on_cancel
2723 .replace(WakeOnCancel::Yielding);
2724 if !old.is_none() {
2725 bail_bug!("thread unexpectedly had wake_on_cancel set");
2726 }
2727
2728 let task = state.get_mut(guest_thread.task)?;
2729 if let Some(event) = task.event {
2734 assert!(matches!(event, Event::None | Event::Cancelled));
2735 } else {
2736 task.event = Some(Event::None);
2737 }
2738 let call = GuestCall {
2739 thread: guest_thread,
2740 kind: GuestCallKind::DeliverEvent {
2741 instance: self,
2742 set: None,
2743 },
2744 };
2745 state.push_low_priority(WorkItem::GuestCall {
2748 instance: self.runtime_instance(runtime_instance),
2749 call,
2750 });
2751 }
2752 callback_code::WAIT => {
2753 let set = get_set(store, set)?;
2754 let state = store.concurrent_state_mut()?;
2755
2756 if state.get_mut(guest_thread.task)?.event.is_some()
2757 || !state.get_mut(set)?.ready.is_empty()
2758 {
2759 state.push_high_priority(WorkItem::GuestCall {
2761 instance: self.runtime_instance(runtime_instance),
2762 call: GuestCall {
2763 thread: guest_thread,
2764 kind: GuestCallKind::DeliverEvent {
2765 instance: self,
2766 set: Some(set),
2767 },
2768 },
2769 });
2770 } else {
2771 let old = state
2779 .get_mut(guest_thread.thread)?
2780 .wake_on_cancel
2781 .replace(WakeOnCancel::Waiting(set));
2782 if !old.is_none() {
2783 bail_bug!("thread unexpectedly had wake_on_cancel set");
2784 }
2785 let old = state
2786 .get_mut(set)?
2787 .waiting
2788 .insert(guest_thread, WaitMode::Callback(self));
2789 if !old.is_none() {
2790 bail_bug!("set's waiting set already had this thread registered");
2791 }
2792 }
2793 }
2794 _ => bail!(Trap::UnsupportedCallbackCode),
2795 }
2796
2797 Ok(())
2798 }
2799
2800 unsafe fn stage_call<T: 'static>(
2807 self,
2808 mut store: StoreContextMut<T>,
2809 guest_thread: QualifiedThreadId,
2810 callee: SendSyncPtr<VMFuncRef>,
2811 param_count: usize,
2812 result_count: usize,
2813 async_: bool,
2814 callback: Option<SendSyncPtr<VMFuncRef>>,
2815 post_return: Option<SendSyncPtr<VMFuncRef>>,
2816 host_caller: bool,
2817 ) -> Result<()> {
2818 unsafe fn make_call<T: 'static>(
2833 store: StoreContextMut<T>,
2834 guest_thread: QualifiedThreadId,
2835 callee: SendSyncPtr<VMFuncRef>,
2836 param_count: usize,
2837 result_count: usize,
2838 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2839 + Send
2840 + Sync
2841 + 'static
2842 + use<T> {
2843 let token = StoreToken::new(store);
2844 move |store: &mut dyn VMStore| {
2845 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2846
2847 store
2848 .concurrent_state_mut()?
2849 .get_mut(guest_thread.thread)?
2850 .state = GuestThreadState::Running;
2851 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2852 let lower = match task.lower_params.take() {
2853 Some(l) => l,
2854 None => bail_bug!("lower_params missing"),
2855 };
2856
2857 lower(store, &mut storage[..param_count])?;
2858
2859 let mut store = token.as_context_mut(store);
2860
2861 unsafe {
2864 crate::Func::call_unchecked_raw(
2865 &mut store,
2866 callee.as_non_null(),
2867 NonNull::new(
2868 &mut storage[..param_count.max(result_count)]
2869 as *mut [MaybeUninit<ValRaw>] as _,
2870 )
2871 .unwrap(),
2872 UncaughtException::Trap,
2873 )?;
2874 }
2875
2876 Ok(storage)
2877 }
2878 }
2879
2880 let call = unsafe {
2884 make_call(
2885 store.as_context_mut(),
2886 guest_thread,
2887 callee,
2888 param_count,
2889 result_count,
2890 )
2891 };
2892
2893 let callee_instance = store
2894 .0
2895 .concurrent_state_mut()?
2896 .get_mut(guest_thread.task)?
2897 .instance;
2898
2899 let fun = if callback.is_some() {
2900 assert!(async_);
2901
2902 Box::new(move |store: &mut dyn VMStore| {
2903 self.add_guest_thread_to_instance_table(
2904 guest_thread.thread,
2905 store,
2906 callee_instance.index,
2907 )?;
2908 let old_thread = store.set_thread(guest_thread)?;
2909 log::trace!(
2910 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2911 );
2912
2913 store.enter_instance(callee_instance);
2914
2915 let storage = call(store)?;
2922
2923 store.exit_instance(callee_instance)?;
2924
2925 store.set_thread(old_thread)?;
2926 let state = store.concurrent_state_mut()?;
2927 if let Some(t) = old_thread.guest() {
2928 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2929 }
2930 log::trace!("stackless call: restored {old_thread:?} as current thread");
2931
2932 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2935
2936 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2937 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2938 } else {
2939 let token = StoreToken::new(store.as_context_mut());
2940 Box::new(move |store: &mut dyn VMStore| {
2941 self.add_guest_thread_to_instance_table(
2942 guest_thread.thread,
2943 store,
2944 callee_instance.index,
2945 )?;
2946 let old_thread = store.set_thread(guest_thread)?;
2947 log::trace!(
2948 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2949 );
2950 let flags = self.id().get(store).instance_flags(callee_instance.index);
2951
2952 let callee_async_typed = store
2953 .concurrent_state_mut()?
2954 .get_mut(guest_thread.task)?
2955 .async_typed;
2956
2957 if !async_ && callee_async_typed {
2961 store.enter_instance(callee_instance);
2962 }
2963
2964 if !callee_async_typed {
2965 store.enter_sync_call(callee_instance)?;
2966 }
2967
2968 let storage = call(store)?;
2975
2976 if !callee_async_typed {
2977 store.exit_sync_call(callee_instance)?;
2978 }
2979
2980 if !async_ {
2981 if callee_async_typed {
2987 store.exit_instance(callee_instance)?;
2988 }
2989
2990 let lift = {
2991 let state = store.concurrent_state_mut()?;
2992 if !state.get_mut(guest_thread.task)?.result.is_none() {
2993 bail_bug!("task has already produced a result");
2994 }
2995
2996 match state.get_mut(guest_thread.task)?.lift_result.take() {
2997 Some(lift) => lift,
2998 None => bail_bug!("lift_result field is missing"),
2999 }
3000 };
3001
3002 let result = (lift.lift)(store, unsafe {
3005 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3006 &storage[..result_count],
3007 )
3008 })?;
3009
3010 let post_return_arg = match result_count {
3011 0 => ValRaw::i32(0),
3012 1 => unsafe { storage[0].assume_init() },
3015 _ => unreachable!(),
3016 };
3017
3018 unsafe {
3019 call_post_return(
3020 token.as_context_mut(store),
3021 post_return.map(|v| v.as_non_null()),
3022 post_return_arg,
3023 flags,
3024 )?;
3025 }
3026
3027 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3028 }
3029
3030 store.set_thread(old_thread)?;
3031
3032 store
3033 .concurrent_state_mut()?
3034 .get_mut(guest_thread.task)?
3035 .exited = true;
3036
3037 log::trace!(
3038 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3039 );
3040
3041 if callee_async_typed {
3042 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3047 }
3048
3049 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
3051 Ok(())
3052 })
3053 };
3054
3055 store.0.concurrent_state_mut()?.push_work_item(
3056 WorkItem::GuestCall {
3057 instance: callee_instance,
3058 call: GuestCall {
3059 thread: guest_thread,
3060 kind: GuestCallKind::StartImplicit(fun),
3061 },
3062 },
3063 if host_caller {
3064 Priority::High
3065 } else {
3066 Priority::Switch
3067 },
3068 )?;
3069
3070 Ok(())
3071 }
3072
3073 unsafe fn prepare_call<T: 'static>(
3086 self,
3087 mut store: StoreContextMut<T>,
3088 start: NonNull<VMFuncRef>,
3089 return_: NonNull<VMFuncRef>,
3090 caller_instance: RuntimeComponentInstanceIndex,
3091 callee_instance: RuntimeComponentInstanceIndex,
3092 task_return_type: TypeTupleIndex,
3093 callee_async_typed: bool,
3094 memory: *mut VMMemoryDefinition,
3095 string_encoding: StringEncoding,
3096 caller_info: CallerInfo,
3097 ) -> Result<()> {
3098 enum ResultInfo {
3099 Heap { results: u32 },
3100 Stack { result_count: u32 },
3101 }
3102
3103 let result_info = match &caller_info {
3104 CallerInfo::Async {
3105 has_result: true,
3106 params,
3107 } => ResultInfo::Heap {
3108 results: match params.last() {
3109 Some(r) => r.get_u32(),
3110 None => bail_bug!("retptr missing"),
3111 },
3112 },
3113 CallerInfo::Async {
3114 has_result: false, ..
3115 } => ResultInfo::Stack { result_count: 0 },
3116 CallerInfo::Sync {
3117 result_count,
3118 params,
3119 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3120 results: match params.last() {
3121 Some(r) => r.get_u32(),
3122 None => bail_bug!("arg ptr missing"),
3123 },
3124 },
3125 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3126 result_count: *result_count,
3127 },
3128 };
3129
3130 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3131
3132 let start = SendSyncPtr::new(start);
3136 let return_ = SendSyncPtr::new(return_);
3137 let token = StoreToken::new(store.as_context_mut());
3138 let old_thread = store.0.current_guest_thread()?;
3139
3140 let state = store.0.concurrent_state_mut()?;
3141
3142 debug_assert_eq!(
3143 state.get_mut(old_thread.task)?.instance,
3144 self.runtime_instance(caller_instance)
3145 );
3146
3147 let guest_thread = GuestTask::new(
3148 state,
3149 Box::new(move |store, dst| {
3150 let mut store = token.as_context_mut(store);
3151 assert!(dst.len() <= MAX_FLAT_PARAMS);
3152 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3154 let count = match caller_info {
3155 CallerInfo::Async { params, has_result } => {
3159 let params = ¶ms[..params.len() - usize::from(has_result)];
3160 for (param, src) in params.iter().zip(&mut src) {
3161 src.write(*param);
3162 }
3163 params.len()
3164 }
3165
3166 CallerInfo::Sync { params, .. } => {
3168 for (param, src) in params.iter().zip(&mut src) {
3169 src.write(*param);
3170 }
3171 params.len()
3172 }
3173 };
3174 unsafe {
3181 crate::Func::call_unchecked_raw(
3182 &mut store,
3183 start.as_non_null(),
3184 NonNull::new(
3185 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3186 )
3187 .unwrap(),
3188 UncaughtException::Trap,
3189 )?;
3190 }
3191 dst.copy_from_slice(&src[..dst.len()]);
3192 let task = store.0.current_guest_thread()?.task;
3193 let state = store.0.concurrent_state_mut()?;
3194 Waitable::Guest(task).set_event(
3195 state,
3196 Some(Event::Subtask {
3197 status: Status::Started,
3198 }),
3199 )?;
3200 Ok(())
3201 }),
3202 LiftResult {
3203 lift: Box::new(move |store, src| {
3204 let mut store = token.as_context_mut(store);
3207 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3209 my_src.push(ValRaw::u32(*results));
3210 }
3211
3212 unsafe {
3219 crate::Func::call_unchecked_raw(
3220 &mut store,
3221 return_.as_non_null(),
3222 my_src.as_mut_slice().into(),
3223 UncaughtException::Trap,
3224 )?;
3225 }
3226
3227 let thread = store.0.current_guest_thread()?;
3228 let state = store.0.concurrent_state_mut()?;
3229 if sync_caller {
3230 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3231 if let ResultInfo::Stack { result_count } = &result_info {
3232 match result_count {
3233 0 => None,
3234 1 => Some(my_src[0]),
3235 _ => unreachable!(),
3236 }
3237 } else {
3238 None
3239 },
3240 );
3241 }
3242 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3243 }),
3244 ty: task_return_type,
3245 memory: NonNull::new(memory).map(SendSyncPtr::new),
3246 string_encoding,
3247 },
3248 Caller::Guest { thread: old_thread },
3249 None,
3250 self.runtime_instance(callee_instance),
3251 callee_async_typed,
3252 false,
3255 )?;
3256
3257 store.0.set_thread(guest_thread)?;
3260 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3261
3262 Ok(())
3263 }
3264
3265 unsafe fn call_callback<T>(
3270 self,
3271 mut store: StoreContextMut<T>,
3272 function: SendSyncPtr<VMFuncRef>,
3273 event: Event,
3274 handle: u32,
3275 ) -> Result<u32> {
3276 let (ordinal, result) = event.parts();
3277 let params = &mut [
3278 ValRaw::u32(ordinal),
3279 ValRaw::u32(handle),
3280 ValRaw::u32(result),
3281 ];
3282 unsafe {
3287 crate::Func::call_unchecked_raw(
3288 &mut store,
3289 function.as_non_null(),
3290 params.as_mut_slice().into(),
3291 UncaughtException::Trap,
3292 )?;
3293 }
3294 Ok(params[0].get_u32())
3295 }
3296
3297 unsafe fn start_call<T: 'static>(
3310 self,
3311 mut store: StoreContextMut<T>,
3312 callback: *mut VMFuncRef,
3313 post_return: *mut VMFuncRef,
3314 callee: NonNull<VMFuncRef>,
3315 param_count: u32,
3316 result_count: u32,
3317 flags: u32,
3318 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3319 ) -> Result<u32> {
3320 let token = StoreToken::new(store.as_context_mut());
3321 let async_caller = storage.is_none();
3322 let guest_thread = store.0.current_guest_thread()?;
3323 let state = store.0.concurrent_state_mut()?;
3324
3325 if !state.event_loop_running {
3326 bail_bug!("Instance::start_call called without a running event loop");
3327 }
3328
3329 let callee = SendSyncPtr::new(callee);
3330 let param_count = usize::try_from(param_count)?;
3331 assert!(param_count <= MAX_FLAT_PARAMS);
3332 let result_count = usize::try_from(result_count)?;
3333 assert!(result_count <= MAX_FLAT_RESULTS);
3334
3335 let task = state.get_mut(guest_thread.task)?;
3336 let callee_async_typed = task.async_typed;
3337 let callee_instance = task.instance;
3338
3339 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3340
3341 if let Some(callback) = NonNull::new(callback) {
3342 let callback = SendSyncPtr::new(callback);
3346 task.callback = Some(Box::new(move |store, event, handle| {
3347 let store = token.as_context_mut(store);
3348 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3349 }));
3350 }
3351
3352 let Caller::Guest { thread: caller } = &task.caller else {
3353 bail_bug!("start_call unexpectedly invoked for host->guest call");
3356 };
3357 let caller = *caller;
3358 let caller_instance = state.get_mut(caller.task)?.instance;
3359
3360 unsafe {
3362 self.stage_call(
3363 store.as_context_mut(),
3364 guest_thread,
3365 callee,
3366 param_count,
3367 result_count,
3368 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3369 NonNull::new(callback).map(SendSyncPtr::new),
3370 NonNull::new(post_return).map(SendSyncPtr::new),
3371 false,
3372 )?;
3373 }
3374
3375 let old_do_not_suspend = if callee_async_typed {
3376 let state = store.0.instance_state(callee_instance).concurrent_state();
3383 let old_do_not_suspend = state.do_not_suspend;
3384 state.do_not_suspend = false;
3385 Some(old_do_not_suspend)
3386 } else {
3387 None
3388 };
3389
3390 let state = store.0.concurrent_state_mut()?;
3391
3392 let guest_waitable = Waitable::Guest(guest_thread.task);
3395 let old_set = guest_waitable.common(state)?.set;
3396 let set = state.get_mut(caller.thread)?.sync_call_set;
3397 guest_waitable.join(state, Some(set))?;
3398
3399 store.0.set_thread(CurrentThread::None)?;
3400
3401 let mut yielded = false;
3417 let (status, waitable) = loop {
3418 store.0.suspend(if yielded {
3419 SuspendReason::Waiting {
3420 set,
3421 thread: caller,
3422 }
3423 } else {
3424 yielded = true;
3425 SuspendReason::YieldingToSubtask { thread: caller }
3426 })?;
3427
3428 if let Some(old_do_not_suspend) = old_do_not_suspend {
3429 store
3430 .0
3431 .instance_state(callee_instance)
3432 .concurrent_state()
3433 .do_not_suspend = old_do_not_suspend;
3434 }
3435
3436 let state = store.0.concurrent_state_mut()?;
3437
3438 log::trace!("taking event for {:?}", guest_thread.task);
3439 let event = guest_waitable.take_event(state)?;
3440 let Some(Event::Subtask { status }) = event else {
3441 bail_bug!("subtasks should only get subtask events, got {event:?}")
3442 };
3443
3444 log::trace!("status {status:?} for {:?}", guest_thread.task);
3445
3446 if status == Status::Returned {
3447 break (status, None);
3449 } else if async_caller {
3450 let handle = store
3454 .0
3455 .instance_state(caller_instance)
3456 .handle_table()
3457 .subtask_insert_guest(guest_thread.task.rep())?;
3458 store
3459 .0
3460 .concurrent_state_mut()?
3461 .get_mut(guest_thread.task)?
3462 .common
3463 .handle = Some(handle);
3464 break (status, Some(handle));
3465 } else {
3466 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3470 }
3471 };
3472
3473 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3474
3475 store.0.set_thread(caller)?;
3477 store
3478 .0
3479 .concurrent_state_mut()?
3480 .get_mut(caller.thread)?
3481 .state = GuestThreadState::Running;
3482 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3483
3484 if let Some(storage) = storage {
3485 let state = store.0.concurrent_state_mut()?;
3489 let task = state.get_mut(guest_thread.task)?;
3490 if let Some(result) = task.sync_result.take()? {
3491 if let Some(result) = result {
3492 storage[0] = MaybeUninit::new(result);
3493 }
3494
3495 if task.exited && task.ready_to_delete() {
3496 Waitable::Guest(guest_thread.task).delete_from(store.0)?;
3497 }
3498 }
3499 }
3500
3501 Ok(status.pack(waitable))
3502 }
3503
3504 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3520 self,
3521 mut store: StoreContextMut<'_, T>,
3522 host_task: EnteredHostTask,
3523 result_may_require_realloc: bool,
3524 future: impl Future<Output = Result<R>> + Send + 'static,
3525 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3526 + Send
3527 + 'static,
3528 ) -> Result<u32> {
3529 let token = StoreToken::new(store.as_context_mut());
3530
3531 let (join_handle, future) = JoinHandle::run(future);
3534 let mut future = Box::pin(future);
3535
3536 let poll = tls::set(store.0, || {
3541 future
3542 .as_mut()
3543 .poll(&mut Context::from_waker(&Waker::noop()))
3544 });
3545
3546 match poll {
3547 Poll::Ready(result) => {
3549 let result = result.transpose()?;
3550 let task = store.0.current_materialized_host_task()?;
3553 lower(store.as_context_mut(), result, true, task)?;
3554 return Ok(Status::Returned.pack(None));
3555 }
3556
3557 Poll::Pending => {}
3559 }
3560
3561 let Some(task) = store.0.materialize_host_task_id()? else {
3565 bail_bug!("current thread is not a host thread")
3566 };
3567 {
3568 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3569 assert!(matches!(state, HostTaskState::CalleeStarted));
3570 *state = HostTaskState::CalleeRunning(join_handle);
3571 }
3572
3573 let future = Box::pin(async move {
3581 let result = match run_with_host_task_set(task, future).await? {
3582 Some(result) => Some(result?),
3583 None => None,
3584 };
3585 let on_complete = move |store: &mut dyn VMStore| {
3586 let mut store = token.as_context_mut(store);
3590 let old = store.0.set_thread(task)?;
3591
3592 let status = if result.is_some() {
3593 Status::Returned
3594 } else {
3595 Status::ReturnCancelled
3596 };
3597
3598 lower(store.as_context_mut(), result, false, Some(task))?;
3599 let state = store.0.concurrent_state_mut()?;
3600 match &mut state.get_mut(task)?.state {
3601 pending @ HostTaskState::CalleeCancelling => {
3604 *pending = HostTaskState::CalleeDone { cancelled: true };
3605 }
3606
3607 other => *other = HostTaskState::CalleeDone { cancelled: false },
3609 }
3610 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3611
3612 store.0.set_thread(old)?;
3613 Ok(())
3614 };
3615
3616 tls::get(move |store| {
3617 if result_may_require_realloc {
3618 store
3623 .concurrent_state_mut()?
3624 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3625 on_complete,
3626 ))));
3627 Ok(())
3628 } else {
3629 on_complete(store)
3632 }
3633 })
3634 });
3635
3636 let caller = match host_task {
3639 Some(caller) => caller,
3640 None => bail_bug!("host task wasn't created but should have been"),
3641 };
3642 let state = store.0.concurrent_state_mut()?;
3643 state.push_future(future);
3644 let instance = state.get_mut(caller.task)?.instance;
3645 let handle = store
3646 .0
3647 .instance_state(instance)
3648 .handle_table()
3649 .subtask_insert_host(task.rep())?;
3650 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3651 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3652
3653 store.0.set_thread(caller)?;
3657 Ok(Status::Started.pack(Some(handle)))
3658 }
3659
3660 pub(crate) fn task_return(
3663 self,
3664 store: &mut dyn VMStore,
3665 ty: TypeTupleIndex,
3666 options: OptionsIndex,
3667 storage: &[ValRaw],
3668 ) -> Result<()> {
3669 let guest_thread = store.current_guest_thread()?;
3670 let state = store.concurrent_state_mut()?;
3671 let lift = state
3672 .get_mut(guest_thread.task)?
3673 .lift_result
3674 .take()
3675 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3676 if !state.get_mut(guest_thread.task)?.result.is_none() {
3677 bail_bug!("task result unexpectedly already set");
3678 }
3679
3680 let CanonicalOptions {
3681 string_encoding,
3682 data_model,
3683 ..
3684 } = &self.id().get(store).component().env_component().options[options];
3685
3686 let invalid = ty != lift.ty
3687 || string_encoding != &lift.string_encoding
3688 || match data_model {
3689 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3690 Some(memory) => {
3691 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3692 let actual = self.id().get(store).runtime_memory(memory);
3693 expected != actual.as_ptr()
3694 }
3695 None => false,
3698 },
3699 CanonicalOptionsDataModel::Gc { .. } => true,
3701 };
3702
3703 if invalid {
3704 bail!(Trap::TaskReturnInvalid);
3705 }
3706
3707 log::trace!("task.return for {guest_thread:?}");
3708
3709 let result = (lift.lift)(store, storage)?;
3710 self.task_complete(store, guest_thread.task, result, Status::Returned)
3711 }
3712
3713 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3715 let guest_thread = store.current_guest_thread()?;
3716 let state = store.concurrent_state_mut()?;
3717 let task = state.get_mut(guest_thread.task)?;
3718 if !task.cancel_request_delivered {
3719 bail!(Trap::TaskCancelNotCancelled);
3720 }
3721 _ = task
3722 .lift_result
3723 .take()
3724 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3725
3726 if !task.result.is_none() {
3727 bail_bug!("task result should not bet set yet");
3728 }
3729
3730 log::trace!("task.cancel for {guest_thread:?}");
3731
3732 self.task_complete(
3733 store,
3734 guest_thread.task,
3735 Box::new(DummyResult),
3736 Status::ReturnCancelled,
3737 )
3738 }
3739
3740 fn task_complete(
3746 self,
3747 store: &mut StoreOpaque,
3748 guest_task: TableId<GuestTask>,
3749 result: Box<dyn Any + Send + Sync>,
3750 status: Status,
3751 ) -> Result<()> {
3752 store
3753 .component_resource_tables(Some(self))?
3754 .validate_scope_exit()?;
3755
3756 let state = store.concurrent_state_mut()?;
3757 let task = state.get_mut(guest_task)?;
3758
3759 if let Caller::Host { tx, .. } = &mut task.caller {
3760 if let Some(tx) = tx.take() {
3761 _ = tx.send(result);
3762 }
3763 } else {
3764 task.result = Some(result);
3765 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3766 }
3767
3768 Ok(())
3769 }
3770
3771 pub(crate) fn waitable_set_new(
3773 self,
3774 store: &mut StoreOpaque,
3775 caller_instance: RuntimeComponentInstanceIndex,
3776 ) -> Result<u32> {
3777 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3778 let handle = store
3779 .instance_state(self.runtime_instance(caller_instance))
3780 .handle_table()
3781 .waitable_set_insert(set.rep())?;
3782 log::trace!("new waitable set {set:?} (handle {handle})");
3783 Ok(handle)
3784 }
3785
3786 pub(crate) fn waitable_set_drop(
3788 self,
3789 store: &mut StoreOpaque,
3790 caller_instance: RuntimeComponentInstanceIndex,
3791 set: u32,
3792 ) -> Result<()> {
3793 let rep = store
3794 .instance_state(self.runtime_instance(caller_instance))
3795 .handle_table()
3796 .waitable_set_remove(set)?;
3797
3798 log::trace!("drop waitable set {rep} (handle {set})");
3799
3800 if !store
3804 .concurrent_state_mut()?
3805 .get_mut(TableId::<WaitableSet>::new(rep))?
3806 .waiting
3807 .is_empty()
3808 {
3809 bail!(Trap::WaitableSetDropHasWaiters);
3810 }
3811
3812 store
3813 .concurrent_state_mut()?
3814 .delete(TableId::<WaitableSet>::new(rep))?;
3815
3816 Ok(())
3817 }
3818
3819 pub(crate) fn waitable_join(
3821 self,
3822 store: &mut StoreOpaque,
3823 caller_instance: RuntimeComponentInstanceIndex,
3824 waitable_handle: u32,
3825 set_handle: u32,
3826 ) -> Result<()> {
3827 let mut instance = self.id().get_mut(store);
3828 let waitable =
3829 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3830
3831 let set = if set_handle == 0 {
3832 None
3833 } else {
3834 let set = instance.instance_states().0[caller_instance]
3835 .handle_table()
3836 .waitable_set_rep(set_handle)?;
3837
3838 let state = store.concurrent_state_mut()?;
3839 if let Some(old) = waitable.common(state)?.set
3840 && state.get_mut(old)?.is_sync_call_set
3841 {
3842 bail!(Trap::WaitableSyncAndAsync);
3843 }
3844
3845 Some(TableId::<WaitableSet>::new(set))
3846 };
3847
3848 log::trace!(
3849 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3850 );
3851
3852 waitable.join(store.concurrent_state_mut()?, set)
3853 }
3854
3855 pub(crate) fn subtask_drop(
3857 self,
3858 store: &mut StoreOpaque,
3859 caller_instance: RuntimeComponentInstanceIndex,
3860 task_id: u32,
3861 ) -> Result<()> {
3862 self.waitable_join(store, caller_instance, task_id, 0)?;
3863
3864 let (rep, is_host) = store
3865 .instance_state(self.runtime_instance(caller_instance))
3866 .handle_table()
3867 .subtask_remove(task_id)?;
3868
3869 let concurrent_state = store.concurrent_state_mut()?;
3870 let (waitable, delete) = if is_host {
3871 let id = TableId::<HostTask>::new(rep);
3872 let task = concurrent_state.get_mut(id)?;
3873 match &task.state {
3874 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
3875 bail!(Trap::SubtaskDropNotResolved)
3876 }
3877 HostTaskState::CalleeDone { .. } => {}
3878 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3879 bail_bug!("invalid state for callee in `subtask.drop`")
3880 }
3881 }
3882
3883 (Waitable::Host(id), true)
3884 } else {
3885 let id = TableId::<GuestTask>::new(rep);
3886 let task = concurrent_state.get_mut(id)?;
3887 if task.lift_result.is_some() {
3888 bail!(Trap::SubtaskDropNotResolved);
3889 }
3890 (
3891 Waitable::Guest(id),
3892 concurrent_state.get_mut(id)?.ready_to_delete(),
3893 )
3894 };
3895
3896 waitable.common(concurrent_state)?.handle = None;
3897
3898 if waitable.take_event(concurrent_state)?.is_some() {
3901 bail!(Trap::SubtaskDropNotResolved);
3902 }
3903
3904 if delete {
3905 waitable.delete_from(store)?;
3906 }
3907
3908 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3909 Ok(())
3910 }
3911
3912 pub(crate) fn waitable_set_wait(
3914 self,
3915 store: &mut StoreOpaque,
3916 options: OptionsIndex,
3917 set: u32,
3918 payload: u32,
3919 ) -> Result<u32> {
3920 let &CanonicalOptions {
3921 instance: caller_instance,
3922 ..
3923 } = &self.id().get(store).component().env_component().options[options];
3924 let caller = self.runtime_instance(caller_instance);
3925 let rep = store
3926 .instance_state(self.runtime_instance(caller_instance))
3927 .handle_table()
3928 .waitable_set_rep(set)?;
3929
3930 self.waitable_check(
3931 store,
3932 caller,
3933 WaitableCheck::Wait,
3934 WaitableCheckParams {
3935 set: TableId::new(rep),
3936 options,
3937 payload,
3938 },
3939 )
3940 }
3941
3942 pub(crate) fn waitable_set_poll(
3944 self,
3945 store: &mut StoreOpaque,
3946 options: OptionsIndex,
3947 set: u32,
3948 payload: u32,
3949 ) -> Result<u32> {
3950 let &CanonicalOptions {
3951 instance: caller_instance,
3952 ..
3953 } = &self.id().get(store).component().env_component().options[options];
3954 let caller = self.runtime_instance(caller_instance);
3955 let rep = store
3956 .instance_state(caller)
3957 .handle_table()
3958 .waitable_set_rep(set)?;
3959
3960 self.waitable_check(
3961 store,
3962 caller,
3963 WaitableCheck::Poll,
3964 WaitableCheckParams {
3965 set: TableId::new(rep),
3966 options,
3967 payload,
3968 },
3969 )
3970 }
3971
3972 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3974 let thread_id = store.current_guest_thread()?.thread;
3975 match store
3976 .concurrent_state_mut()?
3977 .get_mut(thread_id)?
3978 .instance_rep
3979 {
3980 Some(r) => Ok(r),
3981 None => bail_bug!("thread should have instance_rep by now"),
3982 }
3983 }
3984
3985 pub(crate) fn thread_new_indirect<T: 'static>(
3987 self,
3988 mut store: StoreContextMut<T>,
3989 runtime_instance: RuntimeComponentInstanceIndex,
3990 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3992 start_func_idx: u32,
3993 context: i32,
3994 ) -> Result<u32> {
3995 log::trace!("creating new thread");
3996
3997 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3998 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3999 let callee = instance
4000 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4001 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4002 if callee.type_index(store.0) != start_func_ty.type_index() {
4003 bail!(Trap::ThreadNewIndirectInvalidType);
4004 }
4005
4006 let token = StoreToken::new(store.as_context_mut());
4007 let start_func = Box::new(
4008 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4009 let old_thread = store.set_thread(guest_thread)?;
4010 log::trace!(
4011 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4012 );
4013
4014 let mut store = token.as_context_mut(store);
4015 let mut params = [ValRaw::i32(context)];
4016 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4019
4020 store.0.set_thread(old_thread)?;
4021
4022 let runtime_instance = self.runtime_instance(runtime_instance);
4023
4024 store
4027 .0
4028 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4029
4030 store
4031 .0
4032 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
4033
4034 log::trace!("explicit thread {guest_thread:?} completed");
4035 let state = store.0.concurrent_state_mut()?;
4036 if let Some(t) = old_thread.guest() {
4037 state.get_mut(t.thread)?.state = GuestThreadState::Running;
4038 }
4039 log::trace!("thread start: restored {old_thread:?} as current thread");
4040
4041 Ok(())
4042 },
4043 );
4044
4045 let current_thread = store.0.current_guest_thread()?;
4046 let state = store.0.concurrent_state_mut()?;
4047 let parent_task = current_thread.task;
4048
4049 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4050 let thread_id = state.push(new_thread)?;
4051 state.get_mut(parent_task)?.threads.insert(thread_id);
4052
4053 log::trace!("new thread with id {thread_id:?} created");
4054
4055 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4056 }
4057
4058 pub(crate) fn resume_thread(
4059 self,
4060 store: &mut StoreOpaque,
4061 runtime_instance: RuntimeComponentInstanceIndex,
4062 thread_idx: u32,
4063 how: ResumeThread,
4064 ) -> Result<bool> {
4065 let thread_id =
4066 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4067 let state = store.concurrent_state_mut()?;
4068 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4069
4070 if store.current_guest_thread()? == guest_thread {
4071 bail!(Trap::CannotResumeThread);
4072 }
4073
4074 let state = store.concurrent_state_mut()?;
4075 let thread = state.get_mut(guest_thread.thread)?;
4076 let priority = match how {
4077 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
4078 ResumeThread::ResumeLater => Priority::Low,
4079 };
4080
4081 match (&how, &thread.state) {
4082 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
4084 (ResumeThread::Promote, _) => return Ok(false),
4085
4086 (
4089 ResumeThread::Resume | ResumeThread::ResumeLater,
4090 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
4091 ) => {}
4092 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
4093 bail!(Trap::CannotResumeThread)
4094 }
4095 }
4096
4097 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4098 GuestThreadState::NotStartedExplicit(start_func) => {
4099 log::trace!("starting thread {guest_thread:?}");
4100 let guest_call = WorkItem::GuestCall {
4101 instance: self.runtime_instance(runtime_instance),
4102 call: GuestCall {
4103 thread: guest_thread,
4104 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4105 start_func(store, guest_thread)
4106 })),
4107 },
4108 };
4109 store
4110 .concurrent_state_mut()?
4111 .push_work_item(guest_call, priority)?;
4112 }
4113 GuestThreadState::Suspended(fiber) => {
4114 log::trace!("resuming thread {thread_id:?} that was suspended");
4115 store.concurrent_state_mut()?.push_work_item(
4116 WorkItem::ResumeFiber {
4117 instance: self.runtime_instance(runtime_instance),
4118 thread: guest_thread,
4119 fiber,
4120 },
4121 priority,
4122 )?;
4123 }
4124 GuestThreadState::Ready { fiber } => {
4125 log::trace!("resuming thread {thread_id:?} that was ready");
4126 thread.state = GuestThreadState::Ready { fiber };
4127 store
4128 .concurrent_state_mut()?
4129 .promote_thread_work_item(guest_thread)?;
4130 }
4131 other @ (GuestThreadState::NotStartedImplicit
4132 | GuestThreadState::Running
4133 | GuestThreadState::Completed) => {
4134 thread.state = other;
4135 }
4136 }
4137 Ok(true)
4138 }
4139
4140 fn add_guest_thread_to_instance_table(
4141 self,
4142 thread_id: TableId<GuestThread>,
4143 store: &mut StoreOpaque,
4144 runtime_instance: RuntimeComponentInstanceIndex,
4145 ) -> Result<u32> {
4146 let guest_id = store
4147 .instance_state(self.runtime_instance(runtime_instance))
4148 .thread_handle_table()
4149 .guest_thread_insert(thread_id.rep())?;
4150 store
4151 .concurrent_state_mut()?
4152 .get_mut(thread_id)?
4153 .instance_rep = Some(guest_id);
4154 Ok(guest_id)
4155 }
4156
4157 pub(crate) fn suspension_intrinsic(
4161 self,
4162 store: &mut StoreOpaque,
4163 caller: RuntimeComponentInstanceIndex,
4164 yielding: bool,
4165 to_thread: SuspensionTarget,
4166 ) -> Result<WaitResult> {
4167 let check_suspend = match to_thread {
4168 SuspensionTarget::Promote(thread) => {
4169 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4170 }
4171 SuspensionTarget::Resume(thread) => {
4172 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4173 bail_bug!(
4174 "`resume_thread` should only ever return false \
4175 when `ResumeThread::Promote` is passed to it"
4176 );
4177 }
4178 false
4179 }
4180 SuspensionTarget::None => true,
4181 };
4182
4183 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4184 return if yielding {
4185 Ok(WaitResult::Completed)
4186 } else {
4187 Err(Trap::CannotBlockSyncTask.into())
4188 };
4189 }
4190
4191 let guest_thread = store.current_guest_thread()?;
4192
4193 let reason = if yielding {
4194 SuspendReason::Yielding {
4195 thread: guest_thread,
4196 }
4197 } else {
4198 SuspendReason::ExplicitlySuspending {
4199 thread: guest_thread,
4200 }
4201 };
4202
4203 store.suspend(reason)?;
4204
4205 Ok(WaitResult::Completed)
4206 }
4207
4208 fn waitable_check(
4210 self,
4211 store: &mut StoreOpaque,
4212 caller: RuntimeInstance,
4213 check: WaitableCheck,
4214 params: WaitableCheckParams,
4215 ) -> Result<u32> {
4216 let guest_thread = store.current_guest_thread()?;
4217
4218 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4219
4220 let state = store.concurrent_state_mut()?;
4221 let task = state.get_mut(guest_thread.task)?;
4222
4223 match &check {
4226 WaitableCheck::Wait => {
4227 let set = params.set;
4228
4229 if (task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4230 && state.get_mut(set)?.ready.is_empty()
4231 {
4232 store.switch_or_trap_if_may_not_suspend(caller)?;
4233
4234 store.suspend(SuspendReason::Waiting {
4235 set,
4236 thread: guest_thread,
4237 })?;
4238 }
4239 }
4240 WaitableCheck::Poll => {}
4241 }
4242
4243 log::trace!(
4244 "waitable check for {guest_thread:?}; set {:?}, part two",
4245 params.set
4246 );
4247
4248 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4250
4251 let (ordinal, handle, result) = match &check {
4252 WaitableCheck::Wait => {
4253 let (event, waitable) = match event {
4254 Some(p) => p,
4255 None => bail_bug!("event expected to be present"),
4256 };
4257 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4258 let (ordinal, result) = event.parts();
4259 (ordinal, handle, result)
4260 }
4261 WaitableCheck::Poll => {
4262 if let Some((event, waitable)) = event {
4263 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4264 let (ordinal, result) = event.parts();
4265 (ordinal, handle, result)
4266 } else {
4267 log::trace!(
4268 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4269 guest_thread.task,
4270 params.set
4271 );
4272 let (ordinal, result) = Event::None.parts();
4273 (ordinal, 0, result)
4274 }
4275 }
4276 };
4277 let memory = self.options_memory_mut(store, params.options);
4278 let ptr = crate::component::func::validate_inbounds_dynamic(
4279 &CanonicalAbiInfo::POINTER_PAIR,
4280 memory,
4281 &ValRaw::u32(params.payload),
4282 )?;
4283 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4284 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4285 Ok(ordinal)
4286 }
4287
4288 pub(crate) fn subtask_cancel(
4290 self,
4291 store: &mut StoreOpaque,
4292 caller_instance: RuntimeComponentInstanceIndex,
4293 async_: bool,
4294 task_id: u32,
4295 ) -> Result<u32> {
4296 let (rep, is_host) = store
4297 .instance_state(self.runtime_instance(caller_instance))
4298 .handle_table()
4299 .subtask_rep(task_id)?;
4300 let waitable = if is_host {
4301 Waitable::Host(TableId::<HostTask>::new(rep))
4302 } else {
4303 Waitable::Guest(TableId::<GuestTask>::new(rep))
4304 };
4305 let concurrent_state = store.concurrent_state_mut()?;
4306
4307 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4308
4309 waitable.trap_if_in_waitable_set(concurrent_state)?;
4310
4311 let needs_block;
4312 if let Waitable::Host(host_task) = waitable {
4313 let state = &mut concurrent_state.get_mut(host_task)?.state;
4314 match state {
4315 HostTaskState::CalleeRunning(handle) => {
4322 handle.abort();
4323 *state = HostTaskState::CalleeCancelling;
4324 needs_block = true;
4325 }
4326
4327 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4330 bail!(Trap::SubtaskCancelAfterTerminal);
4331 }
4332 HostTaskState::CalleeDone { cancelled: false } => {
4333 *state = HostTaskState::CalleeDone { cancelled: true };
4336 needs_block = false;
4337 }
4338
4339 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4342 bail_bug!("invalid states for host callee")
4343 }
4344 }
4345 } else {
4346 let guest_task = TableId::<GuestTask>::new(rep);
4347 let task = concurrent_state.get_mut(guest_task)?;
4348 if !task.already_lowered_parameters() {
4349 store.cancel_guest_subtask_without_lowered_parameters(
4350 self.runtime_instance(caller_instance),
4351 guest_task,
4352 )?;
4353 return Ok(Status::StartCancelled as u32);
4354 } else if !task.returned_or_cancelled() {
4355 task.event = Some(Event::Cancelled);
4363 let runtime_instance = task.instance;
4364 for thread in task.threads.clone() {
4365 let thread = QualifiedThreadId {
4366 task: guest_task,
4367 thread,
4368 };
4369 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4370
4371 let yield_ = |store: &mut StoreOpaque| {
4372 let state = store.instance_state(runtime_instance).concurrent_state();
4377 let old_do_not_suspend = state.do_not_suspend;
4378 state.do_not_suspend = false;
4379
4380 let caller = store.current_guest_thread()?;
4381
4382 let state = store.concurrent_state_mut()?;
4387 let set = state.get_mut(caller.thread)?.sync_call_set;
4388 waitable.join(state, Some(set))?;
4389
4390 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4391
4392 let state = store.concurrent_state_mut()?;
4393 waitable.join(state, None)?;
4394
4395 store
4396 .instance_state(runtime_instance)
4397 .concurrent_state()
4398 .do_not_suspend = old_do_not_suspend;
4399
4400 Ok::<(), crate::Error>(())
4401 };
4402
4403 match thread_mut.wake_on_cancel.take() {
4404 WakeOnCancel::Waiting(set) => {
4405 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread)
4407 {
4408 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4409 instance: runtime_instance,
4410 call: GuestCall {
4411 thread,
4412 kind: GuestCallKind::DeliverEvent {
4413 instance,
4414 set: None,
4415 },
4416 },
4417 },
4418 other => bail_bug!(
4419 "expected `Some(WaitMode::Callback(_))`; got `{other:?}`"
4420 ),
4421 };
4422 concurrent_state.set_switch_item(item)?;
4423
4424 yield_(store)?;
4425
4426 break;
4427 }
4428 WakeOnCancel::Yielding => {
4429 if concurrent_state.promote_thread_work_item(thread)? {
4430 yield_(store)?;
4431 break;
4432 } else {
4433 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4434 }
4435 }
4436 WakeOnCancel::None => {}
4437 }
4438 }
4439
4440 needs_block = !store
4443 .concurrent_state_mut()?
4444 .get_mut(guest_task)?
4445 .returned_or_cancelled()
4446 } else {
4447 needs_block = false;
4448 }
4449 };
4450
4451 if needs_block {
4455 if async_ {
4456 return Ok(BLOCKED);
4457 }
4458
4459 let old_next_switch_item = {
4462 let state = store.concurrent_state_mut()?;
4463 let item = state.next_switch_item.take();
4464 state.push(item)?
4468 };
4469
4470 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4473
4474 let state = store.concurrent_state_mut()?;
4475 state.next_switch_item = state.delete(old_next_switch_item)?;
4476
4477 }
4479
4480 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4481 if let Some(Event::Subtask {
4482 status: status @ (Status::Returned | Status::ReturnCancelled),
4483 }) = event
4484 {
4485 Ok(status as u32)
4486 } else {
4487 bail!(Trap::SubtaskCancelAfterTerminal);
4488 }
4489 }
4490}
4491
4492pub trait VMComponentAsyncStore {
4500 unsafe fn prepare_call(
4506 &mut self,
4507 instance: Instance,
4508 memory: *mut VMMemoryDefinition,
4509 start: NonNull<VMFuncRef>,
4510 return_: NonNull<VMFuncRef>,
4511 caller_instance: RuntimeComponentInstanceIndex,
4512 callee_instance: RuntimeComponentInstanceIndex,
4513 task_return_type: TypeTupleIndex,
4514 callee_async: bool,
4515 string_encoding: StringEncoding,
4516 result_count: u32,
4517 storage: *mut ValRaw,
4518 storage_len: usize,
4519 ) -> Result<()>;
4520
4521 unsafe fn sync_start(
4524 &mut self,
4525 instance: Instance,
4526 callback: *mut VMFuncRef,
4527 callee: NonNull<VMFuncRef>,
4528 param_count: u32,
4529 storage: *mut MaybeUninit<ValRaw>,
4530 storage_len: usize,
4531 ) -> Result<()>;
4532
4533 unsafe fn async_start(
4536 &mut self,
4537 instance: Instance,
4538 callback: *mut VMFuncRef,
4539 post_return: *mut VMFuncRef,
4540 callee: NonNull<VMFuncRef>,
4541 param_count: u32,
4542 result_count: u32,
4543 flags: u32,
4544 ) -> Result<u32>;
4545
4546 fn future_write(
4548 &mut self,
4549 instance: Instance,
4550 caller: RuntimeComponentInstanceIndex,
4551 ty: TypeFutureTableIndex,
4552 options: OptionsIndex,
4553 future: u32,
4554 address: u32,
4555 ) -> Result<u32>;
4556
4557 fn future_read(
4559 &mut self,
4560 instance: Instance,
4561 caller: RuntimeComponentInstanceIndex,
4562 ty: TypeFutureTableIndex,
4563 options: OptionsIndex,
4564 future: u32,
4565 address: u32,
4566 ) -> Result<u32>;
4567
4568 fn future_drop_writable(
4570 &mut self,
4571 instance: Instance,
4572 ty: TypeFutureTableIndex,
4573 writer: u32,
4574 ) -> Result<()>;
4575
4576 fn stream_write(
4578 &mut self,
4579 instance: Instance,
4580 caller: RuntimeComponentInstanceIndex,
4581 ty: TypeStreamTableIndex,
4582 options: OptionsIndex,
4583 stream: u32,
4584 address: u32,
4585 count: u32,
4586 ) -> Result<u32>;
4587
4588 fn stream_read(
4590 &mut self,
4591 instance: Instance,
4592 caller: RuntimeComponentInstanceIndex,
4593 ty: TypeStreamTableIndex,
4594 options: OptionsIndex,
4595 stream: u32,
4596 address: u32,
4597 count: u32,
4598 ) -> Result<u32>;
4599
4600 fn flat_stream_write(
4603 &mut self,
4604 instance: Instance,
4605 caller: RuntimeComponentInstanceIndex,
4606 ty: TypeStreamTableIndex,
4607 options: OptionsIndex,
4608 payload_size: u32,
4609 payload_align: u32,
4610 stream: u32,
4611 address: u32,
4612 count: u32,
4613 ) -> Result<u32>;
4614
4615 fn flat_stream_read(
4618 &mut self,
4619 instance: Instance,
4620 caller: RuntimeComponentInstanceIndex,
4621 ty: TypeStreamTableIndex,
4622 options: OptionsIndex,
4623 payload_size: u32,
4624 payload_align: u32,
4625 stream: u32,
4626 address: u32,
4627 count: u32,
4628 ) -> Result<u32>;
4629
4630 fn stream_drop_writable(
4632 &mut self,
4633 instance: Instance,
4634 ty: TypeStreamTableIndex,
4635 writer: u32,
4636 ) -> Result<()>;
4637
4638 fn error_context_debug_message(
4640 &mut self,
4641 instance: Instance,
4642 ty: TypeComponentLocalErrorContextTableIndex,
4643 options: OptionsIndex,
4644 err_ctx_handle: u32,
4645 debug_msg_address: u32,
4646 ) -> Result<()>;
4647
4648 fn thread_new_indirect(
4650 &mut self,
4651 instance: Instance,
4652 caller: RuntimeComponentInstanceIndex,
4653 func_ty_idx: TypeFuncIndex,
4654 start_func_table_idx: RuntimeTableIndex,
4655 start_func_idx: u32,
4656 context: i32,
4657 ) -> Result<u32>;
4658}
4659
4660impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4662 unsafe fn prepare_call(
4663 &mut self,
4664 instance: Instance,
4665 memory: *mut VMMemoryDefinition,
4666 start: NonNull<VMFuncRef>,
4667 return_: NonNull<VMFuncRef>,
4668 caller_instance: RuntimeComponentInstanceIndex,
4669 callee_instance: RuntimeComponentInstanceIndex,
4670 task_return_type: TypeTupleIndex,
4671 callee_async: bool,
4672 string_encoding: StringEncoding,
4673 result_count_or_max_if_async: u32,
4674 storage: *mut ValRaw,
4675 storage_len: usize,
4676 ) -> Result<()> {
4677 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4681
4682 unsafe {
4683 instance.prepare_call(
4684 StoreContextMut(self),
4685 start,
4686 return_,
4687 caller_instance,
4688 callee_instance,
4689 task_return_type,
4690 callee_async,
4691 memory,
4692 string_encoding,
4693 match result_count_or_max_if_async {
4694 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4695 params,
4696 has_result: false,
4697 },
4698 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4699 params,
4700 has_result: true,
4701 },
4702 result_count => CallerInfo::Sync {
4703 params,
4704 result_count,
4705 },
4706 },
4707 )
4708 }
4709 }
4710
4711 unsafe fn sync_start(
4712 &mut self,
4713 instance: Instance,
4714 callback: *mut VMFuncRef,
4715 callee: NonNull<VMFuncRef>,
4716 param_count: u32,
4717 storage: *mut MaybeUninit<ValRaw>,
4718 storage_len: usize,
4719 ) -> Result<()> {
4720 unsafe {
4721 instance
4722 .start_call(
4723 StoreContextMut(self),
4724 callback,
4725 ptr::null_mut(),
4726 callee,
4727 param_count,
4728 1,
4729 START_FLAG_ASYNC_CALLEE,
4730 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4734 )
4735 .map(drop)
4736 }
4737 }
4738
4739 unsafe fn async_start(
4740 &mut self,
4741 instance: Instance,
4742 callback: *mut VMFuncRef,
4743 post_return: *mut VMFuncRef,
4744 callee: NonNull<VMFuncRef>,
4745 param_count: u32,
4746 result_count: u32,
4747 flags: u32,
4748 ) -> Result<u32> {
4749 unsafe {
4750 instance.start_call(
4751 StoreContextMut(self),
4752 callback,
4753 post_return,
4754 callee,
4755 param_count,
4756 result_count,
4757 flags,
4758 None,
4759 )
4760 }
4761 }
4762
4763 fn future_write(
4764 &mut self,
4765 instance: Instance,
4766 caller: RuntimeComponentInstanceIndex,
4767 ty: TypeFutureTableIndex,
4768 options: OptionsIndex,
4769 future: u32,
4770 address: u32,
4771 ) -> Result<u32> {
4772 instance
4773 .guest_write(
4774 StoreContextMut(self),
4775 caller,
4776 TransmitIndex::Future(ty),
4777 options,
4778 None,
4779 future,
4780 address,
4781 1,
4782 )
4783 .map(|result| result.encode())
4784 }
4785
4786 fn future_read(
4787 &mut self,
4788 instance: Instance,
4789 caller: RuntimeComponentInstanceIndex,
4790 ty: TypeFutureTableIndex,
4791 options: OptionsIndex,
4792 future: u32,
4793 address: u32,
4794 ) -> Result<u32> {
4795 instance
4796 .guest_read(
4797 StoreContextMut(self),
4798 caller,
4799 TransmitIndex::Future(ty),
4800 options,
4801 None,
4802 future,
4803 address,
4804 1,
4805 )
4806 .map(|result| result.encode())
4807 }
4808
4809 fn stream_write(
4810 &mut self,
4811 instance: Instance,
4812 caller: RuntimeComponentInstanceIndex,
4813 ty: TypeStreamTableIndex,
4814 options: OptionsIndex,
4815 stream: u32,
4816 address: u32,
4817 count: u32,
4818 ) -> Result<u32> {
4819 instance
4820 .guest_write(
4821 StoreContextMut(self),
4822 caller,
4823 TransmitIndex::Stream(ty),
4824 options,
4825 None,
4826 stream,
4827 address,
4828 count,
4829 )
4830 .map(|result| result.encode())
4831 }
4832
4833 fn stream_read(
4834 &mut self,
4835 instance: Instance,
4836 caller: RuntimeComponentInstanceIndex,
4837 ty: TypeStreamTableIndex,
4838 options: OptionsIndex,
4839 stream: u32,
4840 address: u32,
4841 count: u32,
4842 ) -> Result<u32> {
4843 instance
4844 .guest_read(
4845 StoreContextMut(self),
4846 caller,
4847 TransmitIndex::Stream(ty),
4848 options,
4849 None,
4850 stream,
4851 address,
4852 count,
4853 )
4854 .map(|result| result.encode())
4855 }
4856
4857 fn future_drop_writable(
4858 &mut self,
4859 instance: Instance,
4860 ty: TypeFutureTableIndex,
4861 writer: u32,
4862 ) -> Result<()> {
4863 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4864 }
4865
4866 fn flat_stream_write(
4867 &mut self,
4868 instance: Instance,
4869 caller: RuntimeComponentInstanceIndex,
4870 ty: TypeStreamTableIndex,
4871 options: OptionsIndex,
4872 payload_size: u32,
4873 payload_align: u32,
4874 stream: u32,
4875 address: u32,
4876 count: u32,
4877 ) -> Result<u32> {
4878 instance
4879 .guest_write(
4880 StoreContextMut(self),
4881 caller,
4882 TransmitIndex::Stream(ty),
4883 options,
4884 Some(FlatAbi {
4885 size: payload_size,
4886 align: payload_align,
4887 }),
4888 stream,
4889 address,
4890 count,
4891 )
4892 .map(|result| result.encode())
4893 }
4894
4895 fn flat_stream_read(
4896 &mut self,
4897 instance: Instance,
4898 caller: RuntimeComponentInstanceIndex,
4899 ty: TypeStreamTableIndex,
4900 options: OptionsIndex,
4901 payload_size: u32,
4902 payload_align: u32,
4903 stream: u32,
4904 address: u32,
4905 count: u32,
4906 ) -> Result<u32> {
4907 instance
4908 .guest_read(
4909 StoreContextMut(self),
4910 caller,
4911 TransmitIndex::Stream(ty),
4912 options,
4913 Some(FlatAbi {
4914 size: payload_size,
4915 align: payload_align,
4916 }),
4917 stream,
4918 address,
4919 count,
4920 )
4921 .map(|result| result.encode())
4922 }
4923
4924 fn stream_drop_writable(
4925 &mut self,
4926 instance: Instance,
4927 ty: TypeStreamTableIndex,
4928 writer: u32,
4929 ) -> Result<()> {
4930 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4931 }
4932
4933 fn error_context_debug_message(
4934 &mut self,
4935 instance: Instance,
4936 ty: TypeComponentLocalErrorContextTableIndex,
4937 options: OptionsIndex,
4938 err_ctx_handle: u32,
4939 debug_msg_address: u32,
4940 ) -> Result<()> {
4941 instance.error_context_debug_message(
4942 StoreContextMut(self),
4943 ty,
4944 options,
4945 err_ctx_handle,
4946 debug_msg_address,
4947 )
4948 }
4949
4950 fn thread_new_indirect(
4951 &mut self,
4952 instance: Instance,
4953 caller: RuntimeComponentInstanceIndex,
4954 func_ty_idx: TypeFuncIndex,
4955 start_func_table_idx: RuntimeTableIndex,
4956 start_func_idx: u32,
4957 context: i32,
4958 ) -> Result<u32> {
4959 instance.thread_new_indirect(
4960 StoreContextMut(self),
4961 caller,
4962 func_ty_idx,
4963 start_func_table_idx,
4964 start_func_idx,
4965 context,
4966 )
4967 }
4968}
4969
4970type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4971
4972async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
4975where
4976 F: Future,
4977{
4978 let mut future = pin!(future);
4979 future::poll_fn(|cx| {
4980 let old_thread = match tls::get(|store| store.set_thread(task)) {
4981 Ok(thread) => thread,
4982 Err(error) => return Poll::Ready(Err(error)),
4983 };
4984 let result = future.as_mut().poll(cx);
4985 match tls::get(|store| store.set_thread(old_thread)) {
4986 Ok(_) => result.map(Ok),
4987 Err(error) => Poll::Ready(Err(error)),
4988 }
4989 })
4990 .await
4991}
4992
4993pub(crate) struct HostTask {
4997 common: WaitableCommon,
4998
4999 call_context: CallContext,
5002
5003 state: HostTaskState,
5004
5005 group: TaskGroupId,
5006}
5007
5008enum HostTaskState {
5009 CalleeStarted,
5014
5015 CalleeRunning(JoinHandle),
5020
5021 CalleeCancelling,
5025
5026 CalleeFinished(LiftedResult),
5030
5031 CalleeDone { cancelled: bool },
5034}
5035
5036impl HostTask {
5037 fn new(
5038 concurrent_state: &mut ConcurrentState,
5039 state: HostTaskState,
5040 caller: QualifiedThreadId,
5041 ) -> Result<Self> {
5042 let group = concurrent_state.get_mut(caller.task)?.group;
5043 concurrent_state.increment_group_ref_count(group)?;
5044
5045 Ok(Self {
5046 common: WaitableCommon::default(),
5047 call_context: CallContext::default(),
5048 state,
5049 group,
5050 })
5051 }
5052}
5053
5054impl TableDebug for HostTask {
5055 fn type_name() -> &'static str {
5056 "HostTask"
5057 }
5058}
5059
5060type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5061
5062enum Caller {
5064 Host {
5066 tx: Option<oneshot::Sender<LiftedResult>>,
5068 host_future_present: bool,
5071 caller: Option<TableId<HostTask>>,
5075 },
5076 Guest {
5078 thread: QualifiedThreadId,
5080 },
5081}
5082
5083struct LiftResult {
5086 lift: RawLift,
5087 ty: TypeTupleIndex,
5088 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5089 string_encoding: StringEncoding,
5090}
5091
5092#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5097pub(crate) struct QualifiedThreadId {
5098 task: TableId<GuestTask>,
5099 thread: TableId<GuestThread>,
5100}
5101
5102impl QualifiedThreadId {
5103 fn qualify(
5104 state: &mut ConcurrentState,
5105 thread: TableId<GuestThread>,
5106 ) -> Result<QualifiedThreadId> {
5107 Ok(QualifiedThreadId {
5108 task: state.get_mut(thread)?.parent_task,
5109 thread,
5110 })
5111 }
5112}
5113
5114impl fmt::Debug for QualifiedThreadId {
5115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5116 f.debug_tuple("QualifiedThreadId")
5117 .field(&self.task.rep())
5118 .field(&self.thread.rep())
5119 .finish()
5120 }
5121}
5122
5123enum GuestThreadState {
5124 NotStartedImplicit,
5125 NotStartedExplicit(
5126 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5127 ),
5128 Running,
5129 Suspended(StoreFiber<'static>),
5130 Ready {
5131 fiber: StoreFiber<'static>,
5132 },
5133 Completed,
5134}
5135
5136impl fmt::Debug for GuestThreadState {
5137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5138 match self {
5139 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5140 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5141 Self::Running => f.debug_tuple("Running").finish(),
5142 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5143 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5144 Self::Completed => f.debug_tuple("Completed").finish(),
5145 }
5146 }
5147}
5148
5149#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5150enum WakeOnCancel {
5151 None,
5152 Waiting(TableId<WaitableSet>),
5153 Yielding,
5154}
5155
5156impl WakeOnCancel {
5157 fn is_none(self) -> bool {
5158 matches!(self, WakeOnCancel::None)
5159 }
5160
5161 fn replace(&mut self, other: WakeOnCancel) -> Self {
5162 let old = *self;
5163 *self = other;
5164 old
5165 }
5166
5167 fn take(&mut self) -> Self {
5168 self.replace(WakeOnCancel::None)
5169 }
5170}
5171
5172pub struct GuestThread {
5173 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5176 parent_task: TableId<GuestTask>,
5178 wake_on_cancel: WakeOnCancel,
5181 state: GuestThreadState,
5183 instance_rep: Option<u32>,
5186 sync_call_set: TableId<WaitableSet>,
5188 old_do_not_suspend: Option<bool>,
5191}
5192
5193impl GuestThread {
5194 fn from_instance(
5197 state: Pin<&mut ComponentInstance>,
5198 caller_instance: RuntimeComponentInstanceIndex,
5199 guest_thread: u32,
5200 ) -> Result<TableId<Self>> {
5201 let rep = state.instance_states().0[caller_instance]
5202 .thread_handle_table()
5203 .guest_thread_rep(guest_thread)?;
5204 Ok(TableId::new(rep))
5205 }
5206
5207 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5208 let sync_call_set = state.push(WaitableSet {
5209 is_sync_call_set: true,
5210 ..WaitableSet::default()
5211 })?;
5212 Ok(Self {
5213 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5214 parent_task,
5215 wake_on_cancel: WakeOnCancel::None,
5216 state: GuestThreadState::NotStartedImplicit,
5217 instance_rep: None,
5218 sync_call_set,
5219 old_do_not_suspend: None,
5220 })
5221 }
5222
5223 fn new_explicit(
5224 state: &mut ConcurrentState,
5225 parent_task: TableId<GuestTask>,
5226 start_func: Box<
5227 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5228 >,
5229 ) -> Result<Self> {
5230 let sync_call_set = state.push(WaitableSet {
5231 is_sync_call_set: true,
5232 ..WaitableSet::default()
5233 })?;
5234 Ok(Self {
5235 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5236 parent_task,
5237 wake_on_cancel: WakeOnCancel::None,
5238 state: GuestThreadState::NotStartedExplicit(start_func),
5239 instance_rep: None,
5240 sync_call_set,
5241 old_do_not_suspend: None,
5242 })
5243 }
5244}
5245
5246impl TableDebug for GuestThread {
5247 fn type_name() -> &'static str {
5248 "GuestThread"
5249 }
5250}
5251
5252enum SyncResult {
5253 NotProduced,
5254 Produced(Option<ValRaw>),
5255 Taken,
5256}
5257
5258impl SyncResult {
5259 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5260 Ok(match mem::replace(self, SyncResult::Taken) {
5261 SyncResult::NotProduced => None,
5262 SyncResult::Produced(val) => Some(val),
5263 SyncResult::Taken => {
5264 bail_bug!("attempted to take a synchronous result that was already taken")
5265 }
5266 })
5267 }
5268}
5269
5270#[derive(Debug)]
5271enum HostFutureState {
5272 NotApplicable,
5273 Live,
5274 Dropped,
5275}
5276
5277pub(crate) struct GuestTask {
5279 common: WaitableCommon,
5281 lower_params: Option<RawLower>,
5283 lift_result: Option<LiftResult>,
5285 result: Option<LiftedResult>,
5288 callback: Option<CallbackFn>,
5291 caller: Caller,
5293 call_context: CallContext,
5298 sync_result: SyncResult,
5301 cancel_request_delivered: bool,
5305 starting_sent: bool,
5308 instance: RuntimeInstance,
5315 event: Option<Event>,
5318 exited: bool,
5320 threads: HashSet<TableId<GuestThread>>,
5322 host_future_state: HostFutureState,
5325 async_typed: bool,
5328 async_lifted: bool,
5331
5332 decremented_interesting_task_count: bool,
5333
5334 group: TaskGroupId,
5335}
5336
5337impl GuestTask {
5338 fn already_lowered_parameters(&self) -> bool {
5339 self.lower_params.is_none()
5341 }
5342
5343 fn returned_or_cancelled(&self) -> bool {
5344 self.lift_result.is_none()
5346 }
5347
5348 fn ready_to_delete(&self) -> bool {
5349 let threads_completed = self.threads.is_empty();
5350 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5351 let pending_completion_event = matches!(
5352 self.common.event,
5353 Some(Event::Subtask {
5354 status: Status::Returned | Status::ReturnCancelled
5355 })
5356 );
5357 let ready = threads_completed
5358 && !has_sync_result
5359 && !pending_completion_event
5360 && !matches!(self.host_future_state, HostFutureState::Live);
5361 log::trace!(
5362 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5363 threads_completed,
5364 has_sync_result,
5365 pending_completion_event,
5366 self.host_future_state
5367 );
5368 ready
5369 }
5370
5371 fn new(
5372 state: &mut ConcurrentState,
5373 lower_params: RawLower,
5374 lift_result: LiftResult,
5375 caller: Caller,
5376 callback: Option<CallbackFn>,
5377 instance: RuntimeInstance,
5378 async_typed: bool,
5379 async_lifted: bool,
5380 ) -> Result<QualifiedThreadId> {
5381 let host_future_state = match &caller {
5382 Caller::Guest { .. } => HostFutureState::NotApplicable,
5383 Caller::Host {
5384 host_future_present,
5385 ..
5386 } => {
5387 if *host_future_present {
5388 HostFutureState::Live
5389 } else {
5390 HostFutureState::NotApplicable
5391 }
5392 }
5393 };
5394
5395 let group = match caller {
5396 Caller::Guest { thread } => {
5397 let group = state.get_mut(thread.task)?.group;
5398 state.increment_group_ref_count(group)?;
5399 group
5400 }
5401 Caller::Host { .. } => state.make_task_group()?,
5402 };
5403
5404 let task = state.push(Self {
5405 common: WaitableCommon::default(),
5406 lower_params: Some(lower_params),
5407 lift_result: Some(lift_result),
5408 result: None,
5409 callback,
5410 caller,
5411 call_context: CallContext::default(),
5412 sync_result: SyncResult::NotProduced,
5413 cancel_request_delivered: false,
5414 starting_sent: false,
5415 instance,
5416 event: None,
5417 exited: false,
5418 threads: HashSet::new(),
5419 host_future_state,
5420 async_typed,
5421 async_lifted,
5422 decremented_interesting_task_count: false,
5423 group,
5424 })?;
5425 let new_thread = GuestThread::new_implicit(state, task)?;
5426 let thread = state.push(new_thread)?;
5427 state.get_mut(task)?.threads.insert(thread);
5428 state.interesting_tasks += 1;
5429 let thread = QualifiedThreadId { task, thread };
5430 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5431 Ok(thread)
5432 }
5433}
5434
5435impl TableDebug for GuestTask {
5436 fn type_name() -> &'static str {
5437 "GuestTask"
5438 }
5439}
5440
5441#[derive(Default)]
5443struct WaitableCommon {
5444 event: Option<Event>,
5446 set: Option<TableId<WaitableSet>>,
5448 handle: Option<u32>,
5450}
5451
5452#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5454enum Waitable {
5455 Host(TableId<HostTask>),
5457 Guest(TableId<GuestTask>),
5459 Transmit(TableId<TransmitHandle>),
5461}
5462
5463impl Waitable {
5464 fn from_instance(
5467 state: Pin<&mut ComponentInstance>,
5468 caller_instance: RuntimeComponentInstanceIndex,
5469 waitable: u32,
5470 ) -> Result<Self> {
5471 use crate::runtime::vm::component::Waitable;
5472
5473 let (waitable, kind) = state.instance_states().0[caller_instance]
5474 .handle_table()
5475 .waitable_rep(waitable)?;
5476
5477 Ok(match kind {
5478 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5479 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5480 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5481 })
5482 }
5483
5484 fn rep(&self) -> u32 {
5486 match self {
5487 Self::Host(id) => id.rep(),
5488 Self::Guest(id) => id.rep(),
5489 Self::Transmit(id) => id.rep(),
5490 }
5491 }
5492
5493 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5497 log::trace!("waitable {self:?} join set {set:?}");
5498
5499 let old = mem::replace(&mut self.common(state)?.set, set);
5500
5501 if let Some(old) = old {
5502 match *self {
5503 Waitable::Host(id) => state.remove_child(id, old),
5504 Waitable::Guest(id) => state.remove_child(id, old),
5505 Waitable::Transmit(id) => state.remove_child(id, old),
5506 }?;
5507
5508 state.get_mut(old)?.ready.remove(self);
5509 }
5510
5511 if let Some(set) = set {
5512 match *self {
5513 Waitable::Host(id) => state.add_child(id, set),
5514 Waitable::Guest(id) => state.add_child(id, set),
5515 Waitable::Transmit(id) => state.add_child(id, set),
5516 }?;
5517
5518 if self.common(state)?.event.is_some() {
5519 self.mark_ready(state)?;
5520 }
5521 }
5522
5523 Ok(())
5524 }
5525
5526 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5528 Ok(match self {
5529 Self::Host(id) => &mut state.get_mut(*id)?.common,
5530 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5531 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5532 })
5533 }
5534
5535 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5541 if self.common(state)?.set.is_some() {
5542 bail!(Trap::WaitableSyncAndAsync);
5543 }
5544 Ok(())
5545 }
5546
5547 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5551 log::trace!("set event for {self:?}: {event:?}");
5552 self.common(state)?.event = event;
5553 self.mark_ready(state)
5554 }
5555
5556 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5558 let common = self.common(state)?;
5559 let event = common.event.take();
5560 if let Some(set) = self.common(state)?.set {
5561 state.get_mut(set)?.ready.remove(self);
5562 }
5563
5564 Ok(event)
5565 }
5566
5567 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5571 if let Some(set) = self.common(state)?.set {
5572 let set_state = state.get_mut(set)?;
5573 set_state.ready.insert(*self);
5574
5575 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5576 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5577 assert!(wake_on_cancel.is_none() || wake_on_cancel == WakeOnCancel::Waiting(set));
5578
5579 let item = match mode {
5580 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5581 instance: state.get_mut(thread.task)?.instance,
5582 thread,
5583 fiber,
5584 }),
5585 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5586 instance: state.get_mut(thread.task)?.instance,
5587 call: GuestCall {
5588 thread,
5589 kind: GuestCallKind::DeliverEvent {
5590 instance,
5591 set: Some(set),
5592 },
5593 },
5594 }),
5595 };
5596
5597 if let Some(item) = item {
5598 state.push_high_priority(item);
5599 }
5600 }
5601 }
5602 Ok(())
5603 }
5604
5605 fn delete_from(&self, store: &mut StoreOpaque) -> Result<()> {
5607 match self {
5608 Self::Host(task) => {
5609 log::trace!("delete host task {task:?}");
5610 let state = store.concurrent_state_mut()?;
5611 let task = state.delete(*task)?;
5612
5613 state.decrement_group_ref_count(task.group)?;
5614 }
5615 Self::Guest(task) => {
5616 log::trace!("delete guest task {task:?}");
5617 let state = store.concurrent_state_mut()?;
5618 let task = state.delete(*task)?;
5619
5620 state.decrement_group_ref_count(task.group)?;
5621
5622 debug_assert!(task.decremented_interesting_task_count);
5629 }
5630 Self::Transmit(task) => {
5631 store.concurrent_state_mut()?.delete(*task)?;
5632 }
5633 }
5634
5635 Ok(())
5636 }
5637}
5638
5639impl fmt::Debug for Waitable {
5640 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5641 match self {
5642 Self::Host(id) => write!(f, "{id:?}"),
5643 Self::Guest(id) => write!(f, "{id:?}"),
5644 Self::Transmit(id) => write!(f, "{id:?}"),
5645 }
5646 }
5647}
5648
5649#[derive(Default)]
5651struct WaitableSet {
5652 ready: BTreeSet<Waitable>,
5654 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5656 is_sync_call_set: bool,
5659}
5660
5661impl TableDebug for WaitableSet {
5662 fn type_name() -> &'static str {
5663 "WaitableSet"
5664 }
5665}
5666
5667type RawLower =
5669 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5670
5671type RawLift = Box<
5673 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5674>;
5675
5676type LiftedResult = Box<dyn Any + Send + Sync>;
5680
5681struct DummyResult;
5684
5685#[derive(Default)]
5687pub struct ConcurrentInstanceState {
5688 backpressure: u16,
5690 do_not_enter: bool,
5692 do_not_suspend: bool,
5695 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5698}
5699
5700impl ConcurrentInstanceState {
5701 pub fn pending_is_empty(&self) -> bool {
5702 self.pending.is_empty()
5703 }
5704}
5705
5706#[derive(Debug, Copy, Clone)]
5707pub(crate) enum CurrentThread {
5708 Guest(QualifiedThreadId),
5711 Host(TableId<HostTask>),
5713 DeferredHost(QualifiedThreadId),
5716 None,
5719}
5720
5721impl CurrentThread {
5722 fn guest(&self) -> Option<&QualifiedThreadId> {
5723 match self {
5724 Self::Guest(id) => Some(id),
5725 _ => None,
5726 }
5727 }
5728
5729 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5730 match self {
5731 Self::Guest(id) => Some(id.task),
5732 _ => None,
5733 }
5734 }
5735
5736 fn is_none(&self) -> bool {
5737 matches!(self, Self::None)
5738 }
5739}
5740
5741impl From<QualifiedThreadId> for CurrentThread {
5742 fn from(id: QualifiedThreadId) -> Self {
5743 Self::Guest(id)
5744 }
5745}
5746
5747impl From<TableId<HostTask>> for CurrentThread {
5748 fn from(id: TableId<HostTask>) -> Self {
5749 Self::Host(id)
5750 }
5751}
5752
5753enum Priority {
5754 Switch,
5755 High,
5756 Low,
5757}
5758
5759pub struct ConcurrentState {
5761 unforced_current_thread: CurrentThread,
5767
5768 deferred_host_call_context: Option<CallContext>,
5774
5775 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5780 table: AlwaysMut<ResourceTable>,
5782 switch_item: Option<WorkItem>,
5790 next_switch_item: Option<WorkItem>,
5796 high_priority: VecDeque<WorkItem>,
5798 low_priority: VecDeque<WorkItem>,
5800 suspend_reason: Option<SuspendReason>,
5804 worker: Option<StoreFiber<'static>>,
5808 worker_item: Option<WorkerItem>,
5810
5811 global_error_context_ref_counts:
5824 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5825
5826 interesting_tasks: usize,
5839
5840 interesting_tasks_empty_waker: Option<Waker>,
5844
5845 ready_for_concurrent_call_waker: Option<Waker>,
5850
5851 event_loop_running: bool,
5853
5854 #[cfg(feature = "task-group-hook")]
5856 task_group_hook: Option<Box<dyn TaskGroupHook>>,
5857}
5858
5859impl Default for ConcurrentState {
5860 fn default() -> Self {
5861 Self {
5862 unforced_current_thread: CurrentThread::None,
5863 deferred_host_call_context: None,
5864 table: AlwaysMut::new(ResourceTable::new()),
5865 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5866 switch_item: None,
5867 next_switch_item: None,
5868 high_priority: VecDeque::new(),
5869 low_priority: VecDeque::new(),
5870 suspend_reason: None,
5871 worker: None,
5872 worker_item: None,
5873 global_error_context_ref_counts: BTreeMap::new(),
5874 interesting_tasks: 0,
5875 interesting_tasks_empty_waker: None,
5876 ready_for_concurrent_call_waker: None,
5877 event_loop_running: false,
5878 #[cfg(feature = "task-group-hook")]
5879 task_group_hook: None,
5880 }
5881 }
5882}
5883
5884impl ConcurrentState {
5885 pub(crate) fn take_fibers_and_futures(
5902 &mut self,
5903 fibers: &mut Vec<StoreFiber<'static>>,
5904 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5905 ) {
5906 let mut items = Vec::new();
5907 for (_, entry) in self.table.get_mut().iter_mut() {
5908 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5909 for mode in mem::take(&mut set.waiting).into_values() {
5910 match mode {
5911 WaitMode::Fiber(fiber) => {
5912 fibers.push(fiber);
5913 }
5914 WaitMode::Callback(_) => {}
5915 }
5916 }
5917 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5918 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5919 mem::replace(&mut thread.state, GuestThreadState::Completed)
5920 {
5921 fibers.push(fiber);
5922 }
5923 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
5924 if let Some(item) = item.take() {
5925 items.push(item);
5926 }
5927 }
5928 }
5929
5930 if let Some(fiber) = self.worker.take() {
5931 fibers.push(fiber);
5932 }
5933
5934 let mut handle_item = |item| match item {
5935 WorkItem::ResumeFiber { fiber, .. } => {
5936 fibers.push(fiber);
5937 }
5938 WorkItem::PushFuture(future) => {
5939 self.futures
5940 .get_mut()
5941 .as_mut()
5942 .unwrap()
5943 .push(future.into_inner());
5944 }
5945 WorkItem::ResumeThread { .. }
5946 | WorkItem::GuestCall { .. }
5947 | WorkItem::WorkerFunction(_) => {}
5948 };
5949
5950 for item in items {
5951 handle_item(item);
5952 }
5953 if let Some(item) = self.switch_item.take() {
5954 handle_item(item);
5955 }
5956 if let Some(item) = self.next_switch_item.take() {
5957 handle_item(item);
5958 }
5959 for item in mem::take(&mut self.high_priority) {
5960 handle_item(item);
5961 }
5962 for item in mem::take(&mut self.low_priority) {
5963 handle_item(item);
5964 }
5965
5966 if let Some(them) = self.futures.get_mut().take() {
5967 futures.push(them);
5968 }
5969 }
5970
5971 #[cfg(feature = "gc")]
5972 pub(crate) fn trace_fiber_roots(
5973 &mut self,
5974 modules: &ModuleRegistry,
5975 unwind: &dyn Unwind,
5976 gc_roots_list: &mut GcRootsList,
5977 ) {
5978 let ConcurrentState {
5979 table,
5980 worker,
5981 switch_item,
5982 next_switch_item,
5983 high_priority,
5984 low_priority,
5985
5986 futures: _,
5990
5991 worker_item: _,
5993 unforced_current_thread: _,
5994 deferred_host_call_context: _,
5995 suspend_reason: _,
5996 global_error_context_ref_counts: _,
5997 interesting_tasks: _,
5998 interesting_tasks_empty_waker: _,
5999 ready_for_concurrent_call_waker: _,
6000 event_loop_running: _,
6001 #[cfg(feature = "task-group-hook")]
6002 task_group_hook: _,
6003 } = self;
6004
6005 for (_, entry) in table.get_mut().iter_mut() {
6006 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6007 for mode in set.waiting.values_mut() {
6008 match mode {
6009 WaitMode::Fiber(fiber) => {
6010 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6011 }
6012 WaitMode::Callback(_) => {}
6013 }
6014 }
6015 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6016 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
6017 &mut thread.state
6018 {
6019 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6020 }
6021 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6022 entry.downcast_mut::<Option<WorkItem>>()
6023 {
6024 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6025 }
6026 }
6027
6028 if let Some(fiber) = worker {
6029 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6030 }
6031
6032 let mut handle_item = |item: &mut WorkItem| match item {
6033 WorkItem::ResumeFiber { fiber, .. } => {
6034 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6035 }
6036 WorkItem::PushFuture(_future) => {
6037 }
6040 WorkItem::ResumeThread { .. }
6041 | WorkItem::GuestCall { .. }
6042 | WorkItem::WorkerFunction(_) => {}
6043 };
6044
6045 if let Some(item) = switch_item {
6046 handle_item(item);
6047 }
6048 if let Some(item) = next_switch_item {
6049 handle_item(item);
6050 }
6051 for item in high_priority {
6052 handle_item(item);
6053 }
6054 for item in low_priority {
6055 handle_item(item);
6056 }
6057 }
6058
6059 fn push<V: Send + Sync + 'static>(
6060 &mut self,
6061 value: V,
6062 ) -> Result<TableId<V>, ResourceTableError> {
6063 self.table.get_mut().push(value).map(TableId::from)
6064 }
6065
6066 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6067 self.table.get_mut().get_mut(&Resource::from(id))
6068 }
6069
6070 pub fn add_child<T: 'static, U: 'static>(
6071 &mut self,
6072 child: TableId<T>,
6073 parent: TableId<U>,
6074 ) -> Result<(), ResourceTableError> {
6075 self.table
6076 .get_mut()
6077 .add_child(Resource::from(child), Resource::from(parent))
6078 }
6079
6080 pub fn remove_child<T: 'static, U: 'static>(
6081 &mut self,
6082 child: TableId<T>,
6083 parent: TableId<U>,
6084 ) -> Result<(), ResourceTableError> {
6085 self.table
6086 .get_mut()
6087 .remove_child(Resource::from(child), Resource::from(parent))
6088 }
6089
6090 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6091 self.table.get_mut().delete(Resource::from(id))
6092 }
6093
6094 fn push_future(&mut self, future: HostTaskFuture) {
6095 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6102 }
6103
6104 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6105 log::trace!("set switch item: {item:?}");
6106
6107 if self.switch_item.is_some() {
6108 bail_bug!("switch item already set");
6109 }
6110
6111 self.switch_item = Some(item);
6112
6113 Ok(())
6114 }
6115
6116 fn take_next_switch_item(&mut self) -> Result<()> {
6117 if let Some(item) = self.next_switch_item.take() {
6118 self.set_switch_item(item)?;
6119 }
6120 Ok(())
6121 }
6122
6123 fn push_high_priority(&mut self, item: WorkItem) {
6124 log::trace!("push high priority: {item:?}");
6125 self.high_priority.push_front(item);
6126 }
6127
6128 fn push_low_priority(&mut self, item: WorkItem) {
6129 log::trace!("push low priority: {item:?}");
6130 self.low_priority.push_front(item);
6131 }
6132
6133 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6134 match priority {
6135 Priority::Switch => self.set_switch_item(item)?,
6136 Priority::High => self.push_high_priority(item),
6137 Priority::Low => self.push_low_priority(item),
6138 }
6139
6140 Ok(())
6141 }
6142
6143 fn promote_instance_local_thread_work_item(
6144 &mut self,
6145 current_instance: RuntimeInstance,
6146 ) -> Result<bool> {
6147 log::trace!("promote thread work items for {current_instance:?}");
6148
6149 self.promote_work_item_matching(|item: &WorkItem| {
6150 let result = match item {
6151 WorkItem::ResumeThread { instance, .. }
6152 | WorkItem::ResumeFiber { instance, .. }
6153 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6154 _ => false,
6155 };
6156
6157 log::trace!("candidate {item:?}: {result}");
6158 result
6159 })
6160 }
6161
6162 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6163 self.promote_work_item_matching(|item: &WorkItem| match item {
6164 WorkItem::ResumeThread {
6165 thread: item_thread,
6166 ..
6167 }
6168 | WorkItem::GuestCall {
6169 call:
6170 GuestCall {
6171 thread: item_thread,
6172 ..
6173 },
6174 ..
6175 } => *item_thread == thread,
6176 _ => false,
6177 })
6178 }
6179
6180 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6181 where
6182 F: FnMut(&WorkItem) -> bool,
6183 {
6184 for item in mem::take(&mut self.high_priority).into_iter().rev() {
6189 if self.switch_item.is_none() && predicate(&item) {
6190 self.set_switch_item(item)?;
6191 } else {
6192 self.push_high_priority(item);
6193 }
6194 }
6195
6196 if self.switch_item.is_none() {
6197 for item in mem::take(&mut self.low_priority).into_iter().rev() {
6198 if self.switch_item.is_none() && predicate(&item) {
6199 self.set_switch_item(item)?;
6200 } else {
6201 self.push_low_priority(item);
6202 }
6203 }
6204 }
6205
6206 Ok(self.switch_item.is_some())
6207 }
6208
6209 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6212 match task {
6213 Scope::HostId(task) => {
6214 let task: TableId<HostTask> = TableId::new(task);
6215 Ok(&mut self.get_mut(task)?.call_context)
6216 }
6217 Scope::Id(task) => {
6218 let task: TableId<GuestTask> = TableId::new(task);
6219 Ok(&mut self.get_mut(task)?.call_context)
6220 }
6221 }
6222 }
6223
6224 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6225 self.deferred_host_call_context.as_mut()
6226 }
6227
6228 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6229 match self.futures.get_mut().as_mut() {
6230 Some(f) => Ok(f),
6231 None => bail_bug!("futures field of concurrent state is currently taken"),
6232 }
6233 }
6234
6235 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6236 self.table.get_mut()
6237 }
6238
6239 fn debug_assert_deferred_host_invariant(&self) {
6240 debug_assert_eq!(
6241 self.deferred_host_call_context.is_some(),
6242 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6243 "a deferred host thread and call context must exist together",
6244 );
6245 }
6246
6247 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6248 self.debug_assert_deferred_host_invariant();
6249 let caller = match self.unforced_current_thread {
6250 CurrentThread::DeferredHost(caller) => caller,
6251 thread => return Ok(thread),
6252 };
6253
6254 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6256 let task = self.push(task)?;
6257 let call_context = self
6258 .deferred_host_call_context
6259 .take()
6260 .expect("deferred host call context should be present");
6261 self.get_mut(task)
6262 .expect("newly inserted host task should be present")
6263 .call_context = call_context;
6264 self.unforced_current_thread = CurrentThread::Host(task);
6265 self.debug_assert_deferred_host_invariant();
6266 log::trace!("new host task materialized {task:?}");
6267 Ok(CurrentThread::Host(task))
6268 }
6269
6270 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6271 match self.materialize_host_task()? {
6272 CurrentThread::Host(id) => Ok(Some(id)),
6273 CurrentThread::None => Ok(None),
6274 CurrentThread::Guest(_) => {
6275 bail_bug!("tried to materialize a host task id from a guest thread")
6276 }
6277 CurrentThread::DeferredHost(_) => {
6278 bail_bug!(
6279 "current thread is a deferred host thread which should have been materialized"
6280 )
6281 }
6282 }
6283 }
6284
6285 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6286 match self.materialize_host_task()? {
6287 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6288 _ => bail_bug!("current scope is not a deferred host scope"),
6289 }
6290 }
6291}
6292
6293fn for_any_lower<
6296 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6297>(
6298 fun: F,
6299) -> F {
6300 fun
6301}
6302
6303fn for_any_lift<
6305 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6306>(
6307 fun: F,
6308) -> F {
6309 fun
6310}
6311
6312fn check_ambient_store(id: StoreId) {
6313 let message = "\
6314 `Future`s which depend on asynchronous component tasks, streams, or \
6315 futures to complete may only be polled from the event loop of the \
6316 store to which they belong. Please use \
6317 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6318 ";
6319 tls::try_get(|store| {
6320 let matched = match store {
6321 tls::TryGet::Some(store) => store.id() == id,
6322 tls::TryGet::Taken | tls::TryGet::None => false,
6323 };
6324
6325 if !matched {
6326 panic!("{message}")
6327 }
6328 });
6329}
6330
6331fn unpack_callback_code(code: u32) -> (u32, u32) {
6332 (code & 0xF, code >> 4)
6333}
6334
6335struct WaitableCheckParams {
6339 set: TableId<WaitableSet>,
6340 options: OptionsIndex,
6341 payload: u32,
6342}
6343
6344enum WaitableCheck {
6347 Wait,
6348 Poll,
6349}
6350
6351pub(crate) struct PreparedCall<R> {
6353 handle: Func,
6355 thread: QualifiedThreadId,
6357 param_count: usize,
6359 rx: oneshot::Receiver<LiftedResult>,
6362 runtime_instance: RuntimeInstance,
6364 _phantom: PhantomData<R>,
6365}
6366
6367impl<R> PreparedCall<R> {
6368 pub(crate) fn task_id(&self) -> TaskId {
6370 TaskId {
6371 task: self.thread.task,
6372 runtime_instance: self.runtime_instance,
6373 }
6374 }
6375}
6376
6377pub(crate) struct TaskId {
6379 task: TableId<GuestTask>,
6380 runtime_instance: RuntimeInstance,
6381}
6382
6383impl TaskId {
6384 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6390 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6391 let delete = if !task.already_lowered_parameters() {
6392 store.cancel_guest_subtask_without_lowered_parameters(
6393 self.runtime_instance,
6394 self.task,
6395 )?;
6396 true
6397 } else {
6398 task.host_future_state = HostFutureState::Dropped;
6399 task.ready_to_delete()
6400 };
6401 if delete {
6402 Waitable::Guest(self.task).delete_from(store)?
6403 }
6404 Ok(())
6405 }
6406}
6407
6408pub(crate) fn prepare_call<T, R>(
6414 mut store: StoreContextMut<T>,
6415 handle: Func,
6416 param_count: usize,
6417 host_future_present: bool,
6418 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6419 + Send
6420 + Sync
6421 + 'static,
6422 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6423 + Send
6424 + Sync
6425 + 'static,
6426) -> Result<PreparedCall<R>> {
6427 if !store.0.may_enter() {
6428 bail!(Trap::CannotEnterComponent);
6429 }
6430
6431 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6432
6433 let instance = handle.instance().id().get(store.0);
6434 let options = &instance.component().env_component().options[options];
6435 let ty = &instance.component().types()[ty];
6436 let async_typed = ty.async_;
6437 let async_lifted = raw_options.async_;
6438 let task_return_type = ty.results;
6439 let component_instance = raw_options.instance;
6440 let callback = options.callback.map(|i| instance.runtime_callback(i));
6441 let memory = options
6442 .memory()
6443 .map(|i| instance.runtime_memory(i))
6444 .map(SendSyncPtr::new);
6445 let string_encoding = options.string_encoding;
6446 let token = StoreToken::new(store.as_context_mut());
6447 let caller = store.0.materialize_host_task_id()?;
6448 let state = store.0.concurrent_state_mut()?;
6449
6450 let (tx, rx) = oneshot::channel();
6451
6452 let instance = handle.instance().runtime_instance(component_instance);
6453 let thread = GuestTask::new(
6454 state,
6455 Box::new(for_any_lower(move |store, params| {
6456 lower_params(token.as_context_mut(store), params)
6457 })),
6458 LiftResult {
6459 lift: Box::new(for_any_lift(move |store, result| {
6460 lift_result(store, result)
6461 })),
6462 ty: task_return_type,
6463 memory,
6464 string_encoding,
6465 },
6466 Caller::Host {
6467 tx: Some(tx),
6468 host_future_present,
6469 caller,
6470 },
6471 callback.map(|callback| {
6472 let callback = SendSyncPtr::new(callback);
6473 let instance = handle.instance();
6474 Box::new(move |store: &mut dyn VMStore, event, handle| {
6475 let store = token.as_context_mut(store);
6476 unsafe { instance.call_callback(store, callback, event, handle) }
6479 }) as CallbackFn
6480 }),
6481 instance,
6482 async_typed,
6483 async_lifted,
6484 )?;
6485
6486 Ok(PreparedCall {
6487 handle,
6488 thread,
6489 param_count,
6490 runtime_instance: instance,
6491 rx,
6492 _phantom: PhantomData,
6493 })
6494}
6495
6496pub(crate) struct StagedCall<R> {
6497 store: StoreId,
6498 rx: oneshot::Receiver<LiftedResult>,
6499 _marker: PhantomData<fn() -> R>,
6500 group: TaskGroupId,
6501}
6502
6503impl<R> StagedCall<R> {
6504 pub(crate) fn new<T: 'static>(
6511 mut store: StoreContextMut<T>,
6512 prepared: PreparedCall<R>,
6513 ) -> Result<StagedCall<R>> {
6514 let PreparedCall {
6515 handle,
6516 thread,
6517 param_count,
6518 rx,
6519 ..
6520 } = prepared;
6521
6522 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6523
6524 Ok(StagedCall {
6525 store: store.0.id(),
6526 rx,
6527 _marker: PhantomData,
6528 group: store.0.concurrent_state_mut()?.get_mut(thread.task)?.group,
6529 })
6530 }
6531}
6532
6533impl<R> Future for StagedCall<R>
6534where
6535 R: 'static,
6536{
6537 type Output = Result<R>;
6538
6539 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6540 check_ambient_store(self.store);
6541 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6542 Ok(r) => match r.downcast() {
6543 Ok(r) => Ok(*r),
6544 Err(_) => bail_bug!("wrong type of value produced"),
6545 },
6546 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6547 })
6548 }
6549}
6550
6551fn stage_call0<T: 'static>(
6554 store: StoreContextMut<T>,
6555 handle: Func,
6556 guest_thread: QualifiedThreadId,
6557 param_count: usize,
6558) -> Result<()> {
6559 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6560 let is_concurrent = raw_options.async_;
6561 let callback = raw_options.callback;
6562 let instance = handle.instance();
6563 let callee = handle.lifted_core_func(store.0);
6564 let post_return = raw_options
6565 .post_return
6566 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6567 let callback = callback.map(|i| {
6568 let instance = instance.id().get(store.0);
6569 SendSyncPtr::new(instance.runtime_callback(i))
6570 });
6571
6572 log::trace!("queueing call {guest_thread:?}");
6573
6574 unsafe {
6578 instance.stage_call(
6579 store,
6580 guest_thread,
6581 SendSyncPtr::new(callee),
6582 param_count,
6583 1,
6584 is_concurrent,
6585 callback,
6586 post_return.map(SendSyncPtr::new),
6587 true,
6588 )
6589 }
6590}
6591
6592#[cfg(all(test, feature = "cranelift", feature = "wat"))]
6593mod tests {
6594 use super::*;
6595 use crate::component::{Component, Linker};
6596 use crate::store::AsStoreOpaque;
6597 use crate::{Config, Engine};
6598
6599 fn host_subtask(
6600 state: HostTaskState,
6601 event: Option<Event>,
6602 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
6603 let mut config = Config::new();
6604 config.wasm_component_model_async(true);
6605 let engine = Engine::new(&config)?;
6606 let component = Component::new(&engine, "(component)")?;
6607 let mut store = Store::new(&engine, ());
6608 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
6609 let store_opaque = store.as_store_opaque();
6610 let concurrent_state = store_opaque.concurrent_state_mut()?;
6611 let group = concurrent_state.make_task_group()?;
6614 let task = concurrent_state.push(HostTask {
6615 common: WaitableCommon::default(),
6616 call_context: CallContext::default(),
6617 state,
6618 group,
6619 })?;
6620 let handle = store_opaque
6621 .instance_state(instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)))
6622 .handle_table()
6623 .subtask_insert_host(task.rep())?;
6624 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
6625 common.handle = Some(handle);
6626 common.event = event;
6627 Ok((store, instance, task, handle))
6628 }
6629
6630 #[test]
6631 fn host_subtask_drop_during_cancellation() -> Result<()> {
6632 for abort_completed in [false, true] {
6633 let (handle, future) = JoinHandle::run(future::pending::<()>());
6634 let mut future = pin!(future);
6635 let (mut store, instance, task, handle) =
6636 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
6637 let store = store.as_store_opaque();
6638 let caller = RuntimeComponentInstanceIndex::from_u32(0);
6639 assert_eq!(
6640 instance.subtask_cancel(store, caller, true, handle)?,
6641 BLOCKED
6642 );
6643 if abort_completed {
6644 assert!(matches!(
6647 future
6648 .as_mut()
6649 .poll(&mut Context::from_waker(Waker::noop())),
6650 Poll::Ready(None),
6651 ));
6652 }
6653 for async_ in [false, true] {
6654 let err = instance
6655 .subtask_cancel(store, caller, async_, handle)
6656 .unwrap_err();
6657 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
6658 }
6659 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
6660 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
6661 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
6662 }
6663 Ok(())
6664 }
6665
6666 #[test]
6667 fn host_subtask_cancel_after_completion() -> Result<()> {
6668 for async_ in [false, true] {
6669 let (mut store, instance, task, handle) = host_subtask(
6670 HostTaskState::CalleeDone { cancelled: false },
6671 Some(Event::Subtask {
6672 status: Status::Returned,
6673 }),
6674 )?;
6675 let store = store.as_store_opaque();
6676 let caller = RuntimeComponentInstanceIndex::from_u32(0);
6677 assert_eq!(
6678 instance.subtask_cancel(store, caller, async_, handle)?,
6679 Status::Returned as u32,
6680 );
6681 let err = instance
6682 .subtask_cancel(store, caller, async_, handle)
6683 .unwrap_err();
6684 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
6685 instance.subtask_drop(store, caller, handle)?;
6686 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
6687 }
6688 Ok(())
6689 }
6690
6691 #[test]
6692 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
6693 for (cancelled, status) in [
6694 (false, Status::Returned),
6695 (true, Status::Returned),
6696 (true, Status::ReturnCancelled),
6697 ] {
6698 for delivered in [false, true] {
6699 let event = if delivered {
6700 None
6701 } else {
6702 Some(Event::Subtask { status })
6703 };
6704 let (mut store, instance, task, handle) =
6705 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
6706 let store = store.as_store_opaque();
6707 let result = instance.subtask_drop(
6708 store,
6709 RuntimeComponentInstanceIndex::from_u32(0),
6710 handle,
6711 );
6712 if delivered {
6713 result?;
6714 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
6715 } else {
6716 let err = result.unwrap_err();
6717 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
6718 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
6719 }
6720 }
6721 }
6722 Ok(())
6723 }
6724}