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::{
67 CallContext, ComponentInstance, CurrentScope, HandleCounter, InstanceState, Scope,
68};
69use crate::vm::{
70 AlwaysMut, SendSyncPtr, UncaughtException, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore,
71};
72use crate::{AsContext, AsContextMut, Result, StoreContext, StoreContextMut, ValRaw, bail};
73use crate::{Instance as ModuleInstance, bail_bug};
74use alloc::borrow::ToOwned;
75use alloc::collections::btree_map::Entry;
76use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
77use core::any::Any;
78use core::cell::UnsafeCell;
79use core::fmt;
80use core::future;
81use core::future::Future;
82use core::marker::PhantomData;
83use core::mem::{self, ManuallyDrop, MaybeUninit};
84use core::ops::DerefMut;
85use core::pin::{Pin, pin};
86use core::ptr::{self, NonNull};
87use core::task::{Context, Poll, Waker};
88use futures::channel::oneshot;
89use futures::stream::{FuturesUnordered, StreamExt};
90use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
91use table::{TableDebug, TableId};
92use wasmtime_environ::component::{
93 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
94 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
95 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
96 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
97 TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
98};
99use wasmtime_environ::packed_option::ReservedValue;
100use wasmtime_environ::{ModuleInternedTypeIndex, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
101#[cfg(feature = "gc")]
102use wasmtime_unwinder::Unwind;
103
104pub use abort::JoinHandle;
105pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
106pub use future_stream_any::{FutureAny, StreamAny};
107pub use futures_and_streams::{
108 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
109 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
110 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
111};
112pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
113#[cfg(feature = "task-group-hook")]
114pub use task_group_hook::TaskGroupHook;
115pub use task_group_hook::TaskGroupId;
116
117mod abort;
118mod error_contexts;
119mod func;
120mod future_stream_any;
121mod futures_and_streams;
122pub(crate) mod table;
123#[cfg(feature = "task-group-hook")]
124mod task_group_hook;
125#[cfg(not(feature = "task-group-hook"))]
126mod task_group_hook_disabled;
127#[cfg(not(feature = "task-group-hook"))]
128use task_group_hook_disabled as task_group_hook;
129pub(crate) mod tls;
130
131const BLOCKED: u32 = 0xffff_ffff;
134
135#[derive(Clone, Copy, Eq, PartialEq, Debug)]
137pub enum Status {
138 Starting = 0,
139 Started = 1,
140 Returned = 2,
141 StartCancelled = 3,
142 ReturnCancelled = 4,
143}
144
145impl Status {
146 pub fn pack(self, waitable: Option<u32>) -> u32 {
152 assert!(matches!(self, Status::Returned) == waitable.is_none());
153 let waitable = waitable.unwrap_or(0);
154 assert!(waitable < (1 << 28));
155 (waitable << 4) | (self as u32)
156 }
157}
158
159#[derive(Clone, Copy, Debug)]
162enum Event {
163 None,
164 Subtask {
165 status: Status,
166 },
167 StreamRead {
168 code: ReturnCode,
169 pending: Option<(TypeStreamTableIndex, u32)>,
170 },
171 StreamWrite {
172 code: ReturnCode,
173 pending: Option<(TypeStreamTableIndex, u32)>,
174 },
175 FutureRead {
176 code: ReturnCode,
177 pending: Option<(TypeFutureTableIndex, u32)>,
178 },
179 FutureWrite {
180 code: ReturnCode,
181 pending: Option<(TypeFutureTableIndex, u32)>,
182 },
183 Cancelled,
184}
185
186impl Event {
187 fn parts(self) -> (u32, u32) {
192 const EVENT_NONE: u32 = 0;
193 const EVENT_SUBTASK: u32 = 1;
194 const EVENT_STREAM_READ: u32 = 2;
195 const EVENT_STREAM_WRITE: u32 = 3;
196 const EVENT_FUTURE_READ: u32 = 4;
197 const EVENT_FUTURE_WRITE: u32 = 5;
198 const EVENT_CANCELLED: u32 = 6;
199 match self {
200 Event::None => (EVENT_NONE, 0),
201 Event::Cancelled => (EVENT_CANCELLED, 0),
202 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
203 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
204 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
205 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
206 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
207 }
208 }
209}
210
211mod callback_code {
213 pub const EXIT: u32 = 0;
214 pub const YIELD: u32 = 1;
215 pub const WAIT: u32 = 2;
216}
217
218const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
220const START_FLAG_ASYNC_CALLER: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLER 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>),
743 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
744}
745
746impl fmt::Debug for GuestCallKind {
747 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
748 match self {
749 Self::DeliverEvent { instance, set } => f
750 .debug_struct("DeliverEvent")
751 .field("instance", instance)
752 .field("set", set)
753 .finish(),
754 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
755 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
756 }
757 }
758}
759
760#[derive(Copy, Clone, Debug)]
762pub enum SuspensionTarget {
763 Resume(u32),
764 Promote(u32),
765 None,
766}
767
768#[derive(Copy, Clone, Debug)]
770pub enum ResumeThread {
771 Promote,
772 Resume,
773 ResumeLater,
774}
775
776#[derive(Debug)]
778struct GuestCall {
779 thread: QualifiedThreadId,
780 kind: GuestCallKind,
781}
782
783impl GuestCall {
784 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
795 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
796 let async_typed = task.async_typed;
797 let async_lifted = task.async_lifted;
798 let has_callback = task.callback.is_some();
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 if async_typed {
806 let needs_exclusive = !async_lifted || has_callback;
809 !((needs_exclusive && state.do_not_enter) || state.backpressure > 0)
810 } else {
811 true
812 }
813 }
814 GuestCallKind::StartExplicit(_) => true,
815 };
816 log::trace!(
817 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
818 state.do_not_enter,
819 state.backpressure
820 );
821 Ok(ready)
822 }
823}
824
825enum WorkerItem {
827 GuestCall(GuestCall),
828 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
829}
830
831enum WorkItem {
834 PushFuture(AlwaysMut<HostTaskFuture>),
836 ResumeFiber {
838 instance: RuntimeInstance,
839 thread: QualifiedThreadId,
840 fiber: StoreFiber<'static>,
841 },
842 ResumeThread {
844 instance: RuntimeInstance,
845 thread: QualifiedThreadId,
846 },
847 GuestCall {
849 instance: RuntimeInstance,
850 call: GuestCall,
851 },
852 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
854}
855
856struct ResumingFiber<'a> {
862 store: &'a mut StoreOpaque,
863 fiber: Option<StoreFiber<'static>>,
864}
865
866impl Drop for ResumingFiber<'_> {
867 fn drop(&mut self) {
868 if let Some(mut fiber) = self.fiber.take() {
869 fiber.dispose(self.store);
870 }
871 }
872}
873
874impl fmt::Debug for WorkItem {
875 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
876 match self {
877 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
878 Self::ResumeFiber {
879 instance, thread, ..
880 } => f
881 .debug_struct("ResumeFiber")
882 .field("instance", instance)
883 .field("thread", thread)
884 .finish(),
885 Self::ResumeThread { instance, thread } => f
886 .debug_struct("ResumeThread")
887 .field("instance", instance)
888 .field("thread", thread)
889 .finish(),
890 Self::GuestCall { instance, call } => f
891 .debug_struct("GuestCall")
892 .field("instance", instance)
893 .field("call", call)
894 .finish(),
895 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
896 }
897 }
898}
899
900#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
902pub(crate) enum WaitResult {
903 Cancelled,
904 Completed,
905}
906
907pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
915 store: &mut dyn VMStore,
916 host_task: EnteredHostTask,
917 future: impl Future<Output = Result<R>> + Send + 'static,
918) -> Result<R> {
919 let mut future = Box::pin(future);
926 let poll = tls::set(store, || {
927 future
928 .as_mut()
929 .poll(&mut Context::from_waker(&Waker::noop()))
930 });
931
932 let caller = match host_task {
933 Some(caller) => caller,
934 None => bail_bug!("host task wasn't created but should have been"),
935 };
936
937 let task = match poll {
938 Poll::Ready(result) => return result,
940
941 Poll::Pending => {
946 let Some(task) = store.materialize_host_task_id()? else {
947 bail_bug!("current thread is not a host thread")
948 };
949
950 let future = Box::pin(async move {
953 let result = run_with_host_task_set(task, future).await??;
954 tls::get(move |store| {
955 let state = store.concurrent_state_mut()?;
956 let host_state = &mut state.get_mut(task)?.state;
957 assert!(matches!(host_state, HostTaskState::CalleeStarted));
958 *host_state = HostTaskState::CalleeFinished(Box::new(result));
959
960 Waitable::Host(task).set_event(
961 state,
962 Some(Event::Subtask {
963 status: Status::Returned,
964 }),
965 )?;
966
967 Ok(())
968 })
969 }) as HostTaskFuture;
970
971 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
972 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
973
974 let state = store.concurrent_state_mut()?;
975 state.push_future(future);
976
977 let set = state.get_mut(caller.thread)?.sync_call_set;
978 Waitable::Host(task).join(state, Some(set))?;
979
980 store.suspend(SuspendReason::Waiting {
981 set,
982 thread: caller,
983 })?;
984
985 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
989 task
990 }
991 };
992
993 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
995 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
996 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
997 Ok(result) => *result,
998 Err(_) => bail_bug!("host task finished with wrong type of result"),
999 }),
1000 _ => bail_bug!("unexpected host task state after completion"),
1001 }
1002}
1003
1004fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
1006 match call.kind {
1007 GuestCallKind::DeliverEvent { instance, set } => {
1008 if let Some(set) = set {
1011 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1012 }
1013 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1014 Some(pair) => pair,
1015 None => match set {
1016 Some(set) => {
1021 log::trace!(
1022 "event for {:?} on {set:?} no longer present; waiting again",
1023 call.thread
1024 );
1025 return instance.wait_with_callback(
1026 store.store_opaque_mut(),
1027 call.thread,
1028 set,
1029 );
1030 }
1031 None => (Event::None, None),
1036 },
1037 };
1038 let state = store.concurrent_state_mut()?;
1039 if let Some(set) = set
1044 && waitable.is_none()
1045 && !state.get_mut(set)?.ready.is_empty()
1046 {
1047 state.wake_waiter(set)?;
1048 }
1049 let task = state.get_mut(call.thread.task)?;
1050 let runtime_instance = task.instance;
1051 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1052
1053 log::trace!(
1054 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1055 call.thread,
1056 );
1057
1058 instance.handle_callback_code(store, call.thread, runtime_instance, |store| {
1059 invoke_callback(store, call.thread.task, event, handle)
1060 })?;
1061 }
1062 GuestCallKind::StartImplicit(fun) => {
1063 fun(store)?;
1064 }
1065 GuestCallKind::StartExplicit(fun) => {
1066 fun(store)?;
1067 }
1068 }
1069
1070 Ok(())
1071}
1072
1073fn invoke_callback(
1076 store: &mut dyn VMStore,
1077 task: TableId<GuestTask>,
1078 event: Event,
1079 handle: u32,
1080) -> Result<u32> {
1081 let Some(callback) = store.concurrent_state_mut()?.get_mut(task)?.callback.take() else {
1082 bail_bug!("guest task callback field not present")
1083 };
1084
1085 let code = callback(store, event, handle)?;
1086
1087 store.concurrent_state_mut()?.get_mut(task)?.callback = Some(callback);
1088
1089 Ok(code)
1090}
1091
1092impl<T> Store<T> {
1093 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1095 where
1096 T: Send + 'static,
1097 {
1098 ensure!(
1099 self.as_context().0.concurrency_support(),
1100 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1101 );
1102 self.as_context_mut().run_concurrent(fun).await
1103 }
1104
1105 #[doc(hidden)]
1106 pub fn assert_concurrent_state_empty(&mut self) {
1107 self.as_context_mut().assert_concurrent_state_empty();
1108 }
1109
1110 #[doc(hidden)]
1111 pub fn concurrent_state_table_size(&mut self) -> usize {
1112 self.as_context_mut().concurrent_state_table_size()
1113 }
1114
1115 pub fn spawn(
1117 &mut self,
1118 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1119 ) -> Result<JoinHandle>
1120 where
1121 T: 'static,
1122 {
1123 self.as_context_mut().spawn(task)
1124 }
1125}
1126
1127impl<T> StoreContextMut<'_, T> {
1128 #[doc(hidden)]
1139 pub fn assert_concurrent_state_empty(self) {
1140 let store = self.0;
1141 store
1142 .store_data_mut()
1143 .components
1144 .assert_instance_states_empty();
1145 let state = store.concurrent_state_mut().unwrap();
1146 assert!(
1147 state.table.get_mut().is_empty(),
1148 "non-empty table: {:?}",
1149 state.table.get_mut()
1150 );
1151 assert!(state.switch_item.is_none());
1152 assert!(state.next_switch_item.is_none());
1153 assert!(state.high_priority.is_empty());
1154 assert!(state.low_priority.is_empty());
1155 assert!(state.saved_next_switch_items.is_empty());
1156 assert!(state.unforced_current_thread.is_none());
1157 assert!(state.deferred_host_call_context.is_none());
1158 assert!(state.futures_mut().unwrap().is_empty());
1159 assert!(state.global_error_context_ref_counts.is_empty());
1160 }
1161
1162 #[doc(hidden)]
1167 pub fn concurrent_state_table_size(&mut self) -> usize {
1168 self.0.concurrent_state_mut().unwrap().table.get_mut().len()
1169 }
1170
1171 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1181 where
1182 T: 'static,
1183 {
1184 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1185 self.spawn_with_accessor(accessor, task)
1186 }
1187
1188 fn spawn_with_accessor<D>(
1191 self,
1192 accessor: Accessor<T, D>,
1193 task: impl for<'fut> AccessorTask<'fut, T, D>,
1194 ) -> Result<JoinHandle>
1195 where
1196 T: 'static,
1197 D: HasData + ?Sized,
1198 {
1199 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1203 self.0
1204 .concurrent_state_mut()?
1205 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1206 Ok(handle)
1207 }
1208
1209 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1293 where
1294 T: Send + 'static,
1295 {
1296 ensure!(
1297 self.0.concurrency_support(),
1298 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1299 );
1300 self.do_run_concurrent(fun, false).await
1301 }
1302
1303 pub(super) async fn run_concurrent_trap_on_idle<R>(
1304 self,
1305 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1306 ) -> Result<R> {
1307 self.do_run_concurrent(fun, true).await
1308 }
1309
1310 async fn do_run_concurrent<R>(
1311 mut self,
1312 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1313 trap_on_idle: bool,
1314 ) -> Result<R> {
1315 debug_assert!(self.0.concurrency_support());
1316 self.0.check_event_loop_not_running()?;
1317 let token = StoreToken::new(self.as_context_mut());
1318
1319 struct Dropper<'a, T: 'static, V> {
1320 store: StoreContextMut<'a, T>,
1321 value: ManuallyDrop<V>,
1322 }
1323
1324 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1325 fn drop(&mut self) {
1326 self.store
1327 .0
1328 .concurrent_state_mut_already_forced_current_thread()
1329 .event_loop_running = false;
1330
1331 tls::set(self.store.0, || {
1332 unsafe { ManuallyDrop::drop(&mut self.value) }
1337 });
1338 }
1339 }
1340
1341 let accessor = &Accessor::new(token);
1342 self.0
1343 .concurrent_state_mut_already_forced_current_thread()
1344 .event_loop_running = true;
1345 let dropper = &mut Dropper {
1346 store: self,
1347 value: ManuallyDrop::new(fun(accessor)),
1348 };
1349 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1351
1352 let result = dropper
1353 .store
1354 .as_context_mut()
1355 .poll_until(future, trap_on_idle)
1356 .await;
1357
1358 if result.is_err() {
1359 dropper.store.0.set_trapped();
1360 }
1361
1362 result
1363 }
1364
1365 async fn poll_until<R>(
1371 mut self,
1372 mut future: Pin<&mut impl Future<Output = R>>,
1373 trap_on_idle: bool,
1374 ) -> Result<R> {
1375 struct Reset<'a, T: 'static> {
1376 store: StoreContextMut<'a, T>,
1377 futures: Option<FuturesUnordered<HostTaskFuture>>,
1378 }
1379
1380 impl<'a, T> Drop for Reset<'a, T> {
1381 fn drop(&mut self) {
1382 if let Some(futures) = self.futures.take() {
1383 *self
1384 .store
1385 .0
1386 .concurrent_state_mut_already_forced_current_thread()
1387 .futures
1388 .get_mut() = Some(futures);
1389 }
1390 }
1391 }
1392
1393 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1397 let mut turns_without_yield = 0;
1398
1399 loop {
1400 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1404 let mut reset = Reset {
1405 store: self.as_context_mut(),
1406 futures,
1407 };
1408 let mut next = match reset.futures.as_mut() {
1409 Some(f) => pin!(f.next()),
1410 None => bail_bug!("concurrent state missing futures field"),
1411 };
1412
1413 enum PollResult<R> {
1414 Complete(R),
1415 ProcessWork {
1416 ready: Option<WorkItem>,
1417 low_priority: bool,
1418 },
1419 }
1420
1421 let result = future::poll_fn(|cx| {
1422 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1425 return Poll::Ready(Ok(PollResult::Complete(value)));
1426 }
1427
1428 if reset.store.0.component_trapped() {
1437 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1438 }
1439
1440 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1444 Poll::Ready(Some(output)) => {
1445 match output {
1446 Err(e) => return Poll::Ready(Err(e)),
1447 Ok(()) => {}
1448 }
1449 Poll::Ready(true)
1450 }
1451 Poll::Ready(None) => Poll::Ready(false),
1452 Poll::Pending => Poll::Pending,
1453 };
1454
1455 let state = reset.store.0.concurrent_state_mut()?;
1470 let mut ready = state.switch_item.take();
1471 let mut low_priority = false;
1472 if ready.is_none() {
1473 ready = state.high_priority.pop_back();
1474 if ready.is_none() {
1475 ready = state.low_priority.pop_back();
1476 low_priority = true;
1477 }
1478 }
1479 if ready.is_some() {
1480 return Poll::Ready(Ok(PollResult::ProcessWork {
1481 ready,
1482 low_priority,
1483 }));
1484 }
1485
1486 return match next {
1490 Poll::Ready(true) => {
1491 Poll::Ready(Ok(PollResult::ProcessWork {
1497 ready: None,
1498 low_priority: false,
1499 }))
1500 }
1501 Poll::Ready(false) => {
1502 if let Poll::Ready(value) =
1506 tls::set(reset.store.0, || future.as_mut().poll(cx))
1507 {
1508 Poll::Ready(Ok(PollResult::Complete(value)))
1509 } else {
1510 if trap_on_idle {
1516 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1523 Trap::CannotBlockSyncTask.into()
1524 } else {
1525 Trap::AsyncDeadlock.into()
1527 }))
1528 } else {
1529 Poll::Pending
1533 }
1534 }
1535 }
1536 Poll::Pending => Poll::Pending,
1541 };
1542 })
1543 .await;
1544
1545 drop(reset);
1549
1550 match result? {
1551 PollResult::Complete(value) => break Ok(value),
1554 PollResult::ProcessWork {
1557 mut ready,
1558 low_priority,
1559 } => {
1560 if low_priority {
1588 if let Some(item) = ready.take() {
1589 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1590 }
1591 self.0.yield_now().await;
1592 turns_without_yield = 0;
1593 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1594 }
1595
1596 if let Some(item) = ready {
1597 self.as_context_mut().handle_work_item(item).await?;
1598 }
1599
1600 turns_without_yield += 1;
1601 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1602 turns_without_yield = 0;
1603 self.0.yield_now().await;
1604 }
1605 }
1606 }
1607 }
1608 }
1609
1610 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1612 let item = match item {
1616 WorkItem::ResumeFiber {
1617 instance,
1618 thread,
1619 fiber,
1620 } => {
1621 let future = self.0.resume_fiber(fiber);
1622 log::trace!(
1623 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1624 );
1625 return futures::future::Either::Left(future);
1626 }
1627 item => item,
1628 };
1629 futures::future::Either::Right(async move {
1630 log::trace!("handle work item {item:?}");
1631 match item {
1632 WorkItem::PushFuture(future) => {
1633 self.0
1634 .concurrent_state_mut()?
1635 .futures_mut()?
1636 .push(future.into_inner());
1637 }
1638 WorkItem::ResumeFiber { .. } => unreachable!(),
1639 WorkItem::ResumeThread { thread, .. } => {
1640 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1641 if !matches!(slot, GuestThreadState::Ready { .. }) {
1642 bail_bug!("cannot resume non-pending thread {thread:?}");
1643 }
1644 let GuestThreadState::Ready { fiber } =
1645 mem::replace(slot, GuestThreadState::Running)
1646 else {
1647 unreachable!()
1648 };
1649 self.0.resume_fiber(fiber).await?;
1650 }
1651 WorkItem::GuestCall { call, .. } => {
1652 if call.is_ready(self.0)? {
1653 self.0
1654 .concurrent_state_mut()?
1655 .get_mut(call.thread.thread)?
1656 .wake_on_cancel = WakeOnCancel::None;
1657 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1658 } else {
1659 let state = self.0.concurrent_state_mut()?;
1660 let task = state.get_mut(call.thread.task)?;
1661 if !task.starting_sent {
1662 task.starting_sent = true;
1663 if let GuestCallKind::StartImplicit(_) = &call.kind {
1664 Waitable::Guest(call.thread.task).set_event(
1665 state,
1666 Some(Event::Subtask {
1667 status: Status::Starting,
1668 }),
1669 )?;
1670 }
1671 }
1672
1673 let instance = state.get_mut(call.thread.task)?.instance;
1674 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1683 let state = self.0.concurrent_state_mut()?;
1684 if !state.get_mut(*set)?.ready.is_empty() {
1685 state.wake_waiter(*set)?;
1686 }
1687 }
1688 self.0
1689 .instance_state(instance)
1690 .concurrent_state()
1691 .pending
1692 .insert(call.thread, call.kind);
1693
1694 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1698 }
1699 }
1700 WorkItem::WorkerFunction(fun) => {
1701 self.run_on_worker(WorkerItem::Function(fun)).await?;
1702 }
1703 }
1704
1705 Ok(())
1706 })
1707 }
1708
1709 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1711 let state = self.0.concurrent_state_mut()?;
1712 if state.worker_item.is_some() {
1713 bail_bug!("worker item already set when taking worker fiber");
1714 }
1715 let worker = if let Some(fiber) = state.worker.take() {
1716 fiber
1717 } else {
1718 unsafe {
1737 fiber::make_fiber_unchecked(self.0, move |store| {
1738 loop {
1739 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1740 bail_bug!("worker_item not present when resuming fiber")
1741 };
1742 match item {
1743 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1744 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1745 }
1746
1747 store.suspend(SuspendReason::NeedWork)?;
1748 }
1749 })?
1750 }
1751 };
1752
1753 self.0
1756 .concurrent_state_mut_already_forced_current_thread()
1757 .worker_item = Some(item);
1758
1759 self.0.resume_fiber(worker).await
1760 }
1761
1762 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1767 where
1768 T: 'static,
1769 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1770 + Send
1771 + Sync
1772 + 'static,
1773 R: Send + Sync + 'static,
1774 {
1775 let token = StoreToken::new(self);
1776 async move {
1777 let mut accessor = Accessor::new(token);
1778 closure(&mut accessor).await
1779 }
1780 }
1781
1782 pub(crate) async fn start_instance(
1783 &mut self,
1784 instance: ModuleInstance,
1785 callee: Option<RuntimeInstance>,
1786 ) -> Result<ModuleInstance> {
1787 self.0.check_event_loop_not_running()?;
1789 let (tx, rx) = oneshot::channel();
1790 let token = StoreToken::new(self.as_context_mut());
1791 self.0.queue_task(move |store| {
1792 _ = tx.send(
1793 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1794 .map(|()| instance),
1795 );
1796 Ok(())
1797 })?;
1798 self.as_context_mut()
1799 .run_concurrent_trap_on_idle(async |_| {
1800 rx.await
1801 .map_err(|_| format_err!("oneshot channel canceled"))
1802 })
1803 .await??
1804 }
1805}
1806
1807pub type EnteredHostTask = Option<QualifiedThreadId>;
1814
1815impl StoreOpaque {
1816 pub(crate) fn check_event_loop_not_running(&mut self) -> Result<()> {
1817 if self
1818 .concurrent_state_mut_already_forced_current_thread()
1819 .event_loop_running
1820 {
1821 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1822 }
1823 Ok(())
1824 }
1825
1826 #[inline]
1830 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1831 if !self.concurrency_support() {
1833 return Ok(CurrentThread::None);
1834 }
1835
1836 if !self
1839 .vm_store_context_mut()
1840 .current_thread_mut()
1841 .is_deferred()
1842 {
1843 return Ok(self
1844 .concurrent_state_mut_already_forced_current_thread()
1845 .unforced_current_thread);
1846 }
1847
1848 self.force_deferred_current_thread()
1849 }
1850
1851 #[cold]
1854 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1855 let state = self.concurrent_state_mut_without_forcing_current_thread();
1864 let id = match state.unforced_current_thread.guest_task() {
1865 Some(task) => state.get_mut(task)?.instance.instance,
1866 None => bail_bug!("deferred component-model thread with non-guest base"),
1867 };
1868
1869 let mut frames = Vec::new();
1872 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1873 while let Some(ptr) = cur.as_deferred() {
1874 let deferred = unsafe { ptr.as_non_null().as_ref() };
1879 frames.push((
1880 deferred.callee_async != 0,
1881 deferred.callee_instance,
1882 deferred.saved_context,
1883 ));
1884 cur = deferred.parent;
1885 }
1886
1887 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1891
1892 let current_context = *self.vm_store_context_mut().component_context_mut();
1895
1896 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1900 *self.vm_store_context_mut().component_context_mut() = saved_context;
1904 let callee = RuntimeInstance {
1905 instance: id,
1906 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1907 };
1908 self.enter_guest_sync_call(callee_async, callee)?;
1909 }
1910
1911 *self.vm_store_context_mut().component_context_mut() = current_context;
1913
1914 Ok(self
1915 .concurrent_state_mut_without_forcing_current_thread()
1916 .unforced_current_thread)
1917 }
1918
1919 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1920 match self.current_thread()?.guest() {
1921 Some(id) => Ok(*id),
1922 None => bail_bug!("current thread is not a guest thread"),
1923 }
1924 }
1925
1926 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1930 match self.current_thread()? {
1931 CurrentThread::Host(id) => Ok(Some(id)),
1932 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1933 _ => bail_bug!("current thread is not a host thread"),
1934 }
1935 }
1936
1937 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1940 Ok(self
1941 .concurrent_state_mut()?
1942 .materialize_current_host_task_id()?)
1943 }
1944
1945 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1946 log::trace!("enter sync-typed call {callee:?}");
1947 let state = self.instance_state(callee).concurrent_state();
1948 let old_do_not_suspend = state.do_not_suspend;
1949 state.do_not_suspend = true;
1950
1951 let thread = self.current_guest_thread()?;
1952 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1953 if thread.old_do_not_suspend.is_some() {
1954 bail_bug!("current thread already has `old_do_not_suspend` value");
1955 }
1956
1957 thread.old_do_not_suspend = Some(old_do_not_suspend);
1958
1959 Ok(())
1960 }
1961
1962 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1963 log::trace!("exit sync-typed call {callee:?}");
1964 let thread = self.current_guest_thread()?;
1965 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1966 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1967 bail_bug!("current thread missing `old_do_not_suspend` value");
1968 };
1969 let state = self.instance_state(callee).concurrent_state();
1970 state.do_not_suspend = old_do_not_suspend;
1971 Ok(())
1972 }
1973
1974 pub(crate) fn enter_guest_sync_call(
1986 &mut self,
1987 callee_async_typed: bool,
1988 callee: RuntimeInstance,
1989 ) -> Result<()> {
1990 log::trace!("enter sync-lifted call {callee:?}");
1991 if !self.concurrency_support() {
1992 return self.enter_call_not_concurrent();
1993 }
1994
1995 let state = self.concurrent_state_mut()?;
1999 let item = state.next_switch_item.take();
2000 state.saved_next_switch_items.push(item);
2001
2002 let thread = self.current_thread()?;
2003 let caller = if let Some(thread) = thread.guest() {
2004 Caller::Guest { thread: *thread }
2005 } else {
2006 Caller::Host {
2007 tx: None,
2008 caller: self.materialize_host_task_id()?,
2009 }
2010 };
2011
2012 let state = self.concurrent_state_mut()?;
2013 let guest_thread = GuestTask::new(
2014 state,
2015 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2016 LiftResult {
2017 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2018 ty: TypeTupleIndex::reserved_value(),
2019 memory: None,
2020 string_encoding: StringEncoding::Utf8,
2021 },
2022 caller,
2023 None,
2024 callee,
2025 callee_async_typed,
2026 false,
2027 )?;
2028
2029 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2030 guest_thread.thread,
2031 self,
2032 callee.index,
2033 )?;
2034 self.set_thread(guest_thread)?;
2035
2036 if !callee_async_typed {
2037 self.enter_sync_call(callee)?;
2038 }
2039
2040 Ok(())
2041 }
2042
2043 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2051 if !self.concurrency_support() {
2052 return Ok(self.exit_call_not_concurrent());
2053 }
2054
2055 let thread = match self.current_thread()?.guest() {
2056 Some(t) => *t,
2057 None => bail_bug!("expected task when exiting"),
2058 };
2059 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2060 let instance = task.instance;
2061
2062 let caller = match &task.caller {
2063 &Caller::Guest { thread } => thread.into(),
2064 &Caller::Host { caller, .. } => caller
2065 .map(CurrentThread::Host)
2066 .unwrap_or(CurrentThread::None),
2067 };
2068 task.lift_result = None;
2069 task.exited = true;
2070 let async_typed = task.async_typed;
2071
2072 if !async_typed {
2073 self.exit_sync_call(instance)?;
2074 }
2075
2076 self.set_thread(caller)?;
2077
2078 log::trace!("exit sync-lifted call {instance:?}");
2079
2080 if async_typed {
2081 self.switch_or_trap_if_may_not_suspend(instance)?;
2086 }
2087
2088 self.cleanup_thread(thread, instance)?;
2089
2090 self.concurrent_state_mut()?
2091 .decrement_task_ref_count(thread.task)?;
2092
2093 let state = self.concurrent_state_mut()?;
2094 let Some(item) = state.saved_next_switch_items.pop() else {
2095 bail_bug!("unable to pop from `saved_next_switch_items`");
2096 };
2097 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2098 state.push_high_priority(item);
2101 bail_bug!("`next_switch_item` unexpectedly already set");
2102 }
2103
2104 Ok(())
2105 }
2106
2107 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2114 if !self.concurrency_support() {
2115 self.enter_call_not_concurrent()?;
2116 return Ok(None);
2117 }
2118 let caller = self.current_guest_thread()?;
2119 log::trace!("new deferred host task with caller {caller:?}");
2120
2121 self.set_thread(CurrentThread::DeferredHost(caller))?;
2122 let state = self.concurrent_state_mut()?;
2123 debug_assert!(state.deferred_host_call_context.is_none());
2124 state.deferred_host_call_context = Some(CallContext::default());
2125 state.debug_assert_deferred_host_invariant();
2126 Ok(Some(caller))
2127 }
2128
2129 pub(crate) fn host_task_delete(
2136 &mut self,
2137 original_task: EnteredHostTask,
2138 materialized_task: Option<TableId<HostTask>>,
2139 ) -> Result<()> {
2140 match original_task {
2141 Some(caller) => {
2142 self.set_thread(caller)?;
2143 if materialized_task.is_none() {
2144 let state = self.concurrent_state_mut()?;
2145 let context = state
2146 .deferred_host_call_context
2147 .take()
2148 .expect("deferred host call context should be present");
2149 debug_assert!(context.is_empty());
2150 state.debug_assert_deferred_host_invariant();
2151 }
2152 log::trace!(
2153 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2154 );
2155 if let Some(task) = materialized_task {
2156 self.concurrent_state_mut()?.delete_host_task(task)?;
2157 }
2158 }
2159 None => {
2160 debug_assert!(materialized_task.is_none());
2161 self.exit_call_not_concurrent();
2162 }
2163 }
2164 Ok(())
2165 }
2166
2167 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2170 self.component_instance_mut(instance.instance)
2171 .instance_state(instance.index)
2172 }
2173
2174 fn instance_state_and_handle_counter(
2177 &mut self,
2178 instance: RuntimeInstance,
2179 ) -> (&mut InstanceState, &mut HandleCounter) {
2180 let (component, counter) = self
2181 .store_data_mut()
2182 .component_instance_and_handle_counter_mut(instance.instance);
2183 (component.instance_state(instance.index), counter)
2184 }
2185
2186 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2192 let thread = thread.into();
2193 let state = self.concurrent_state_mut()?;
2194 state.debug_assert_deferred_host_invariant();
2195 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2196
2197 state.handle_thread_switch(old_thread, thread)?;
2198
2199 if let Some(old_thread) = old_thread.guest() {
2207 let old_context = *self.vm_store_context_mut().component_context_mut();
2208 self.concurrent_state_mut()?
2209 .get_mut(old_thread.thread)?
2210 .context = old_context;
2211 }
2212 if cfg!(debug_assertions) {
2213 *self.vm_store_context_mut().component_context_mut() =
2214 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2215 }
2216 if let Some(thread) = thread.guest() {
2217 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2218 let context = thread.context;
2219 if cfg!(debug_assertions) {
2220 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2221 }
2222 *self.vm_store_context_mut().component_context_mut() = context;
2223 }
2224
2225 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2227 VMLazyThread::none()
2228 } else {
2229 VMLazyThread::forced()
2230 };
2231
2232 Ok(old_thread)
2233 }
2234
2235 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2237 if self.switch_if_may_not_suspend(instance)? {
2238 Ok(())
2239 } else {
2240 Err(Trap::CannotBlockSyncTask.into())
2241 }
2242 }
2243
2244 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2248 self.concurrent_state_mut()?;
2252
2253 Ok(!self.concurrency_support()
2254 || !self
2255 .instance_state(instance)
2256 .concurrent_state()
2257 .do_not_suspend
2258 || self
2259 .concurrent_state_mut()?
2260 .promote_instance_local_thread_work_item(instance)?)
2261 }
2262
2263 fn enter_instance(&mut self, instance: RuntimeInstance) {
2267 log::trace!("enter {instance:?}");
2268 self.instance_state(instance)
2269 .concurrent_state()
2270 .do_not_enter = true;
2271 }
2272
2273 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2277 log::trace!("exit {instance:?}");
2278 self.instance_state(instance)
2279 .concurrent_state()
2280 .do_not_enter = false;
2281 self.partition_pending(instance)
2282 }
2283
2284 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2292 for (thread, kind) in
2293 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2294 {
2295 let call = GuestCall { thread, kind };
2296 if call.is_ready(self)? {
2297 self.concurrent_state_mut()?
2298 .push_high_priority(WorkItem::GuestCall { instance, call });
2299 } else {
2300 self.instance_state(instance)
2301 .concurrent_state()
2302 .pending
2303 .insert(call.thread, call.kind);
2304 }
2305 }
2306
2307 if let Some(waker) = self
2308 .concurrent_state_mut()?
2309 .ready_for_concurrent_call_waker
2310 .take()
2311 {
2312 waker.wake();
2313 }
2314
2315 Ok(())
2316 }
2317
2318 pub(crate) fn backpressure_modify(
2320 &mut self,
2321 caller_instance: RuntimeInstance,
2322 modify: impl FnOnce(u16) -> Option<u16>,
2323 ) -> Result<()> {
2324 let state = self.instance_state(caller_instance).concurrent_state();
2325 let old = state.backpressure;
2326 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2327 state.backpressure = new;
2328
2329 if old > 0 && new == 0 {
2330 self.partition_pending(caller_instance)?;
2333 }
2334
2335 Ok(())
2336 }
2337
2338 fn resume_fiber(
2341 &mut self,
2342 fiber: StoreFiber<'static>,
2343 ) -> impl Future<Output = Result<()>> + '_ {
2344 let mut resuming = ResumingFiber {
2345 store: self,
2346 fiber: Some(fiber),
2347 };
2348 async move {
2349 let old_thread = resuming.store.current_thread()?;
2350 log::trace!("resume_fiber: save current thread {old_thread:?}");
2351
2352 let fiber = resuming.fiber.take().unwrap();
2353 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2354
2355 resuming.store.set_thread(old_thread)?;
2356
2357 let state = resuming.store.concurrent_state_mut()?;
2358
2359 if let Some(ot) = old_thread.guest() {
2360 state.set_thread_running(ot.thread)?;
2361 }
2362 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2363
2364 if resuming.fiber.is_some() {
2365 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2366 let reason = match state.suspend_reason.take() {
2368 Some(r) => r,
2369 None => bail_bug!("suspend reason missing when resuming fiber"),
2370 };
2371 match reason {
2372 SuspendReason::NeedWork => {
2373 if state.worker.is_none() {
2374 state.worker = resuming.fiber.take();
2375 }
2376 }
2377 SuspendReason::Yielding { thread } => {
2378 let slot = &mut state.get_mut(thread.thread)?.state;
2379 slot.check_no_fiber()?;
2380 *slot = GuestThreadState::Ready {
2381 fiber: resuming.fiber.take().unwrap(),
2382 };
2383 let instance = state.get_mut(thread.task)?.instance;
2384 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2385 }
2386 SuspendReason::ExplicitlySuspending { thread } => {
2387 let slot = &mut state.get_mut(thread.thread)?.state;
2388 slot.check_no_fiber()?;
2389 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2390 }
2391 SuspendReason::Waiting { set, thread } => {
2392 match state.get_mut(set)?.waiting.entry(thread) {
2393 Entry::Vacant(entry) => {
2394 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2395 }
2396 Entry::Occupied(_) => {
2397 bail_bug!("entry unexpectedly already exists for {thread:?}");
2398 }
2399 }
2400 }
2401 SuspendReason::YieldingToSubtask { thread } => {
2402 let instance = state.get_mut(thread.task)?.instance;
2411 if state.next_switch_item.is_some() {
2412 bail_bug!(
2415 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2416 a thread wanted to wait on a subtask"
2417 );
2418 }
2419 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2420 instance,
2421 thread,
2422 fiber: resuming.fiber.take().unwrap(),
2423 });
2424 log::trace!("set next switch item to {item:?}");
2425 }
2426 };
2427 } else {
2428 log::trace!("resume_fiber: fiber has exited");
2429 }
2430
2431 Ok(())
2432 }
2433 }
2434
2435 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2441 log::trace!("suspend fiber: {reason:?}");
2442
2443 if !self.fiber_async_state_mut().can_block() {
2446 return Err(format_err!("future dropped"));
2447 }
2448
2449 let state = self.concurrent_state_mut()?;
2450
2451 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2458 SuspendReason::Yielding { .. }
2459 | SuspendReason::Waiting { .. }
2460 | SuspendReason::ExplicitlySuspending { .. } => {
2461 if state.switch_item.is_none() {
2464 state.take_next_switch_item()?;
2465 }
2466
2467 (true, false)
2468 }
2469 SuspendReason::YieldingToSubtask { .. } => (true, true),
2470 SuspendReason::NeedWork => (false, false),
2471 };
2472
2473 let old_next_switch_item = if save_and_restore_next_switch_item {
2474 Some(state.save_next_switch_item()?)
2475 } else {
2476 None
2477 };
2478
2479 let old_guest_thread = if save_and_restore_thread {
2480 self.current_thread()?
2481 } else {
2482 CurrentThread::None
2483 };
2484
2485 let waiting_set = match &reason {
2486 SuspendReason::Waiting { set, .. } => Some(*set),
2487 _ => None,
2488 };
2489
2490 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2491 if suspend_reason.is_some() {
2492 bail_bug!("suspend reason unexpectedly already set for store");
2493 }
2494 *suspend_reason = Some(reason);
2495
2496 if let Some(set) = waiting_set {
2499 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2500 }
2501
2502 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2503
2504 if let Some(set) = waiting_set {
2505 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2506 }
2507
2508 if save_and_restore_thread {
2509 self.set_thread(old_guest_thread)?;
2510 }
2511
2512 if let Some(item) = old_next_switch_item {
2513 let state = self.concurrent_state_mut()?;
2514 state.restore_next_switch_item(item)?;
2515 }
2516
2517 Ok(())
2518 }
2519
2520 fn wait_for_event(
2521 &mut self,
2522 caller_instance: RuntimeInstance,
2523 waitable: Waitable,
2524 ) -> Result<()> {
2525 let caller = self.current_guest_thread()?;
2526 let state = self.concurrent_state_mut()?;
2527
2528 waitable.trap_if_in_waitable_set(state)?;
2529
2530 let set = state.get_mut(caller.thread)?.sync_call_set;
2531 waitable.join(state, Some(set))?;
2532
2533 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2534
2535 self.suspend(SuspendReason::Waiting {
2536 set,
2537 thread: caller,
2538 })?;
2539 let state = self.concurrent_state_mut()?;
2540
2541 waitable.join(state, None)
2542 }
2543
2544 fn cleanup_thread(
2566 &mut self,
2567 guest_thread: QualifiedThreadId,
2568 runtime_instance: RuntimeInstance,
2569 ) -> Result<()> {
2570 let state = self.concurrent_state_mut()?;
2571 state.take_next_switch_item()?;
2574 let thread_data = state.get_mut(guest_thread.thread)?;
2575 let sync_call_set = thread_data.sync_call_set;
2576 if let Some(guest_id) = thread_data.instance_rep {
2577 self.instance_state(runtime_instance)
2578 .thread_handle_table()
2579 .guest_thread_remove(guest_id)?;
2580 }
2581 let state = self.concurrent_state_mut()?;
2582
2583 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2585 if let Some(Event::Subtask {
2586 status: Status::Returned | Status::ReturnCancelled,
2587 }) = waitable.common(state)?.event
2588 {
2589 waitable.delete_from(state)?;
2590 }
2591 }
2592
2593 state.delete(guest_thread.thread)?;
2594 state.delete(sync_call_set)?;
2595 let task = state.get_mut(guest_thread.task)?;
2596 task.threads.remove(&guest_thread.thread);
2597
2598 if task.threads.is_empty() && !task.returned_or_cancelled() {
2599 bail!(Trap::NoAsyncResult);
2600 }
2601
2602 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2603 task.decremented_interesting_task_count = true;
2604
2605 debug_assert!(state.interesting_tasks > 0);
2606 state.interesting_tasks -= 1;
2607 if state.interesting_tasks == 0
2608 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2609 {
2610 waker.wake();
2611 }
2612 }
2613
2614 state.decrement_task_ref_count(guest_thread.task)?;
2615
2616 Ok(())
2617 }
2618
2619 fn cancel_guest_subtask_without_lowered_parameters(
2628 &mut self,
2629 caller_instance: RuntimeInstance,
2630 guest_task: TableId<GuestTask>,
2631 ) -> Result<()> {
2632 let concurrent_state = self.concurrent_state_mut()?;
2633 let task = concurrent_state.get_mut(guest_task)?;
2634 assert!(!task.already_lowered_parameters());
2635 task.lower_params = None;
2639 task.lift_result = None;
2640 task.exited = true;
2641 let instance = task.instance;
2642
2643 assert_eq!(1, task.threads.len());
2650 let thread = *task.threads.iter().next().unwrap();
2651 let next_switch_item = concurrent_state.save_next_switch_item()?;
2652 self.cleanup_thread(
2653 QualifiedThreadId {
2654 task: guest_task,
2655 thread,
2656 },
2657 caller_instance,
2658 )?;
2659 self.concurrent_state_mut()?
2660 .restore_next_switch_item(next_switch_item)?;
2661
2662 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2664 let pending_count = pending.len();
2665 pending.retain(|thread, _| thread.task != guest_task);
2666
2667 if pending.len() == pending_count {
2668 let matches = |item: &WorkItem| match item {
2671 WorkItem::ResumeThread { thread, .. }
2672 | WorkItem::GuestCall {
2673 call: GuestCall { thread, .. },
2674 ..
2675 } => thread.task == guest_task,
2676 _ => false,
2677 };
2678
2679 let remove = |queue: &mut VecDeque<WorkItem>| {
2680 let count = queue.len();
2681 queue.retain(|item| !matches(item));
2682 count != queue.len()
2683 };
2684
2685 let state = self.concurrent_state_mut()?;
2686 let mut found = remove(&mut state.high_priority);
2687 found |= remove(&mut state.low_priority);
2688
2689 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2690 if matches!(item, Some(item) if matches(item)) {
2691 found = true;
2692 *item = None;
2693 }
2694 }
2695
2696 if !found {
2697 bail!(Trap::SubtaskCancelAfterTerminal);
2698 }
2699 }
2700 Ok(())
2701 }
2702
2703 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2706 if !self.concurrency_support() {
2707 return Ok(self
2708 .current_scope_id_not_concurrent()?
2709 .map(|id| CurrentScope::Id(Scope::Id(id))));
2710 }
2711
2712 Ok(match self.current_thread()? {
2713 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2714 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2715 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2716 CurrentThread::None => return Ok(None),
2717 })
2718 }
2719
2720 pub(crate) fn queue_task(
2721 &mut self,
2722 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2723 ) -> Result<()> {
2724 self.concurrent_state_mut()?
2725 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2726 Ok(())
2727 }
2728
2729 fn any_may_not_suspend(&mut self) -> Result<bool> {
2738 Ok(self
2746 .concurrent_state_mut()?
2747 .table
2748 .get_mut()
2749 .iter_mut()
2750 .filter_map(|(_, entry)| {
2751 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2752 Some(task.instance)
2753 } else {
2754 None
2755 }
2756 })
2757 .collect::<Vec<_>>()
2758 .into_iter()
2759 .any(|instance| {
2760 self.instance_state(instance)
2761 .concurrent_state()
2762 .do_not_suspend
2763 }))
2764 }
2765}
2766
2767impl Instance {
2768 fn get_event(
2771 self,
2772 store: &mut StoreOpaque,
2773 guest_task: TableId<GuestTask>,
2774 set: Option<TableId<WaitableSet>>,
2775 cancellable: bool,
2776 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2777 let state = store.concurrent_state_mut()?;
2778
2779 let task = state.get_mut(guest_task)?;
2780 let event = &mut task.event;
2781 if let Some(ev) = event
2782 && (cancellable || !matches!(ev, Event::Cancelled))
2783 {
2784 log::trace!("deliver event {ev:?} to {guest_task:?}");
2785
2786 if matches!(ev, Event::Cancelled) {
2787 task.cancel_request_delivered = true;
2788 }
2789
2790 let ev = *ev;
2791 *event = None;
2792 return Ok(Some((ev, None)));
2793 }
2794
2795 let set = match set {
2796 Some(set) => set,
2797 None => return Ok(None),
2798 };
2799 let waitable = match state.get_mut(set)?.ready.pop_first() {
2800 Some(v) => v,
2801 None => return Ok(None),
2802 };
2803
2804 let common = waitable.common(state)?;
2805 let handle = match common.handle {
2806 Some(h) => h,
2807 None => bail_bug!("handle not set when delivering event"),
2808 };
2809 let event = match common.event.take() {
2810 Some(e) => e,
2811 None => bail_bug!("event not set when delivering event"),
2812 };
2813
2814 log::trace!(
2815 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2816 );
2817
2818 waitable.on_delivery(store, self, event)?;
2819
2820 Ok(Some((event, Some((waitable, handle)))))
2821 }
2822
2823 fn handle_callback_code(
2830 self,
2831 store: &mut dyn VMStore,
2832 guest_thread: QualifiedThreadId,
2833 runtime_instance: RuntimeInstance,
2834 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2835 ) -> Result<()> {
2836 let old_thread = store.set_thread(guest_thread)?;
2837 log::trace!(
2838 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2839 );
2840
2841 store.enter_instance(runtime_instance);
2842
2843 let mut code = call(store)?;
2844
2845 if matches!(
2851 unpack_callback_code(code).0,
2852 callback_code::YIELD | callback_code::WAIT
2853 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2854 {
2855 let (unpacked, set) = unpack_callback_code(code);
2856 log::trace!(
2857 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2858 cancellation; delivering it via callback"
2859 );
2860 code = invoke_callback(store, guest_thread.task, event, 0)?;
2861 }
2862
2863 store.exit_instance(runtime_instance)?;
2864
2865 store.set_thread(old_thread)?;
2866 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2867
2868 let store = store.store_opaque_mut();
2869 let (code, set) = unpack_callback_code(code);
2870
2871 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2872
2873 let state = store.concurrent_state_mut()?;
2874
2875 state.take_next_switch_item()?;
2876
2877 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2878 let set = store
2879 .instance_state(runtime_instance)
2880 .handle_table()
2881 .waitable_set_rep(handle)?;
2882
2883 Ok(TableId::<WaitableSet>::new(set))
2884 };
2885
2886 match code {
2887 callback_code::EXIT => {
2888 log::trace!("implicit thread {guest_thread:?} completed");
2889 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2890 task.exited = true;
2891 task.callback = None;
2892
2893 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2898
2899 store.cleanup_thread(guest_thread, runtime_instance)?;
2900 }
2901 callback_code::YIELD => {
2902 let old = state
2905 .get_mut(guest_thread.thread)?
2906 .wake_on_cancel
2907 .replace(WakeOnCancel::Yielding);
2908 if !old.is_none() {
2909 bail_bug!("thread unexpectedly had wake_on_cancel set");
2910 }
2911
2912 let call = GuestCall {
2919 thread: guest_thread,
2920 kind: GuestCallKind::DeliverEvent {
2921 instance: self,
2922 set: None,
2923 },
2924 };
2925 state.push_low_priority(WorkItem::GuestCall {
2928 instance: runtime_instance,
2929 call,
2930 });
2931 }
2932 callback_code::WAIT => {
2933 let set = get_set(store, set)?;
2934 self.wait_with_callback(store, guest_thread, set)?;
2935 }
2936 _ => bail!(Trap::UnsupportedCallbackCode),
2937 }
2938
2939 Ok(())
2940 }
2941
2942 fn wait_with_callback(
2948 self,
2949 store: &mut StoreOpaque,
2950 guest_thread: QualifiedThreadId,
2951 set: TableId<WaitableSet>,
2952 ) -> Result<()> {
2953 let state = store.concurrent_state_mut()?;
2954 state.get_mut(set)?.num_waiting += 1;
2957
2958 if state.get_mut(guest_thread.task)?.event.is_some()
2959 || !state.get_mut(set)?.ready.is_empty()
2960 {
2961 let instance = state.get_mut(guest_thread.task)?.instance;
2963 state.push_high_priority(WorkItem::GuestCall {
2964 instance,
2965 call: GuestCall {
2966 thread: guest_thread,
2967 kind: GuestCallKind::DeliverEvent {
2968 instance: self,
2969 set: Some(set),
2970 },
2971 },
2972 });
2973 return Ok(());
2974 }
2975
2976 let instance = store
2982 .concurrent_state_mut()?
2983 .get_mut(guest_thread.task)?
2984 .instance;
2985 store.switch_or_trap_if_may_not_suspend(instance)?;
2986
2987 let state = store.concurrent_state_mut()?;
2993 let old = state
2994 .get_mut(guest_thread.thread)?
2995 .wake_on_cancel
2996 .replace(WakeOnCancel::Waiting(set));
2997 if !old.is_none() {
2998 bail_bug!("thread unexpectedly had wake_on_cancel set");
2999 }
3000 let old = state
3001 .get_mut(set)?
3002 .waiting
3003 .insert(guest_thread, WaitMode::Callback(self));
3004 if !old.is_none() {
3005 bail_bug!("set's waiting set already had this thread registered");
3006 }
3007 Ok(())
3008 }
3009
3010 unsafe fn stage_call<T: 'static>(
3017 self,
3018 mut store: StoreContextMut<T>,
3019 guest_thread: QualifiedThreadId,
3020 callee: SendSyncPtr<VMFuncRef>,
3021 param_count: usize,
3022 result_count: usize,
3023 async_: bool,
3024 callback: Option<SendSyncPtr<VMFuncRef>>,
3025 post_return: Option<SendSyncPtr<VMFuncRef>>,
3026 host_caller: bool,
3027 ) -> Result<()> {
3028 unsafe fn make_call<T: 'static>(
3043 store: StoreContextMut<T>,
3044 guest_thread: QualifiedThreadId,
3045 callee: SendSyncPtr<VMFuncRef>,
3046 param_count: usize,
3047 result_count: usize,
3048 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3049 + Send
3050 + Sync
3051 + 'static
3052 + use<T> {
3053 let token = StoreToken::new(store);
3054 move |store: &mut dyn VMStore| {
3055 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3056
3057 store
3058 .concurrent_state_mut()?
3059 .set_thread_running(guest_thread.thread)?;
3060 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3061 let lower = match task.lower_params.take() {
3062 Some(l) => l,
3063 None => bail_bug!("lower_params missing"),
3064 };
3065
3066 lower(store, &mut storage[..param_count])?;
3067
3068 let mut store = token.as_context_mut(store);
3069
3070 unsafe {
3073 crate::Func::call_unchecked_raw(
3074 &mut store,
3075 callee.as_non_null(),
3076 NonNull::new(
3077 &mut storage[..param_count.max(result_count)]
3078 as *mut [MaybeUninit<ValRaw>] as _,
3079 )
3080 .unwrap(),
3081 UncaughtException::Trap,
3082 )?;
3083 }
3084
3085 Ok(storage)
3086 }
3087 }
3088
3089 let call = unsafe {
3093 make_call(
3094 store.as_context_mut(),
3095 guest_thread,
3096 callee,
3097 param_count,
3098 result_count,
3099 )
3100 };
3101
3102 let callee_instance = store
3103 .0
3104 .concurrent_state_mut()?
3105 .get_mut(guest_thread.task)?
3106 .instance;
3107
3108 let fun = if callback.is_some() {
3109 assert!(async_);
3110
3111 Box::new(move |store: &mut dyn VMStore| {
3112 self.add_guest_thread_to_instance_table(
3113 guest_thread.thread,
3114 store,
3115 callee_instance.index,
3116 )?;
3117 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3118 let storage = call(store)?;
3119
3120 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3123 })
3124 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3125 } else {
3126 let token = StoreToken::new(store.as_context_mut());
3127 Box::new(move |store: &mut dyn VMStore| {
3128 self.add_guest_thread_to_instance_table(
3129 guest_thread.thread,
3130 store,
3131 callee_instance.index,
3132 )?;
3133 let old_thread = store.set_thread(guest_thread)?;
3134 log::trace!(
3135 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3136 );
3137 let flags = self.id().get(store).instance_flags(callee_instance.index);
3138
3139 let callee_async_typed = store
3140 .concurrent_state_mut()?
3141 .get_mut(guest_thread.task)?
3142 .async_typed;
3143
3144 if !async_ && callee_async_typed {
3148 store.enter_instance(callee_instance);
3149 }
3150
3151 if !callee_async_typed {
3152 store.enter_sync_call(callee_instance)?;
3153 }
3154
3155 let storage = call(store)?;
3162
3163 if !callee_async_typed {
3164 store.exit_sync_call(callee_instance)?;
3165 }
3166
3167 if !async_ {
3168 if callee_async_typed {
3174 store.exit_instance(callee_instance)?;
3175 }
3176
3177 let lift = {
3178 let state = store.concurrent_state_mut()?;
3179 if !state.get_mut(guest_thread.task)?.result.is_none() {
3180 bail_bug!("task has already produced a result");
3181 }
3182
3183 match state.get_mut(guest_thread.task)?.lift_result.take() {
3184 Some(lift) => lift,
3185 None => bail_bug!("lift_result field is missing"),
3186 }
3187 };
3188
3189 let result = (lift.lift)(store, unsafe {
3192 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3193 &storage[..result_count],
3194 )
3195 })?;
3196
3197 let post_return_arg = match result_count {
3198 0 => ValRaw::i32(0),
3199 1 => unsafe { storage[0].assume_init() },
3202 _ => unreachable!(),
3203 };
3204
3205 unsafe {
3206 call_post_return(
3207 token.as_context_mut(store),
3208 post_return.map(|v| v.as_non_null()),
3209 post_return_arg,
3210 flags,
3211 )?;
3212 }
3213
3214 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3215 }
3216
3217 store.set_thread(old_thread)?;
3218
3219 store
3220 .concurrent_state_mut()?
3221 .get_mut(guest_thread.task)?
3222 .exited = true;
3223
3224 log::trace!(
3225 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3226 );
3227
3228 if callee_async_typed {
3229 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3234 }
3235
3236 store.cleanup_thread(guest_thread, callee_instance)?;
3238 Ok(())
3239 })
3240 };
3241
3242 store.0.concurrent_state_mut()?.push_work_item(
3243 WorkItem::GuestCall {
3244 instance: callee_instance,
3245 call: GuestCall {
3246 thread: guest_thread,
3247 kind: GuestCallKind::StartImplicit(fun),
3248 },
3249 },
3250 if host_caller {
3251 Priority::High
3252 } else {
3253 Priority::Switch
3254 },
3255 )?;
3256
3257 Ok(())
3258 }
3259
3260 unsafe fn prepare_call<T: 'static>(
3273 self,
3274 mut store: StoreContextMut<T>,
3275 start: NonNull<VMFuncRef>,
3276 return_: NonNull<VMFuncRef>,
3277 caller_instance: RuntimeComponentInstanceIndex,
3278 callee_instance: RuntimeComponentInstanceIndex,
3279 task_return_type: TypeTupleIndex,
3280 callee_async_typed: bool,
3281 memory: *mut VMMemoryDefinition,
3282 string_encoding: StringEncoding,
3283 caller_info: CallerInfo,
3284 ) -> Result<()> {
3285 enum ResultInfo {
3286 Heap { results: u32 },
3287 Stack { result_count: u32 },
3288 }
3289
3290 let result_info = match &caller_info {
3291 CallerInfo::Async {
3292 has_result: true,
3293 params,
3294 } => ResultInfo::Heap {
3295 results: match params.last() {
3297 Some(r) => r.get_u32(),
3298 None => bail_bug!("retptr missing"),
3299 },
3300 },
3301 CallerInfo::Async {
3302 has_result: false, ..
3303 } => ResultInfo::Stack { result_count: 0 },
3304 CallerInfo::Sync {
3305 result_count,
3306 params,
3307 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3308 results: match params.last() {
3310 Some(r) => r.get_u32(),
3311 None => bail_bug!("arg ptr missing"),
3312 },
3313 },
3314 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3315 result_count: *result_count,
3316 },
3317 };
3318
3319 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3320
3321 let start = SendSyncPtr::new(start);
3325 let return_ = SendSyncPtr::new(return_);
3326 let token = StoreToken::new(store.as_context_mut());
3327 let old_thread = store.0.current_guest_thread()?;
3328
3329 let state = store.0.concurrent_state_mut()?;
3330
3331 debug_assert_eq!(
3332 state.get_mut(old_thread.task)?.instance,
3333 self.runtime_instance(caller_instance)
3334 );
3335
3336 let guest_thread = GuestTask::new(
3337 state,
3338 Box::new(move |store, dst| {
3339 let mut store = token.as_context_mut(store);
3340 assert!(dst.len() <= MAX_FLAT_PARAMS);
3341 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3343 let count = match caller_info {
3344 CallerInfo::Async { params, has_result } => {
3348 let params = ¶ms[..params.len() - usize::from(has_result)];
3349 for (param, src) in params.iter().zip(&mut src) {
3350 src.write(*param);
3351 }
3352 params.len()
3353 }
3354
3355 CallerInfo::Sync { params, .. } => {
3357 for (param, src) in params.iter().zip(&mut src) {
3358 src.write(*param);
3359 }
3360 params.len()
3361 }
3362 };
3363 unsafe {
3370 crate::Func::call_unchecked_raw(
3371 &mut store,
3372 start.as_non_null(),
3373 NonNull::new(
3374 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3375 )
3376 .unwrap(),
3377 UncaughtException::Trap,
3378 )?;
3379 }
3380 dst.copy_from_slice(&src[..dst.len()]);
3381 let task = store.0.current_guest_thread()?.task;
3382 let state = store.0.concurrent_state_mut()?;
3383 Waitable::Guest(task).set_event(
3384 state,
3385 Some(Event::Subtask {
3386 status: Status::Started,
3387 }),
3388 )?;
3389 Ok(())
3390 }),
3391 LiftResult {
3392 lift: Box::new(move |store, src| {
3393 let mut store = token.as_context_mut(store);
3396 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3398 my_src.push(ValRaw::u32(*results));
3399 }
3400
3401 unsafe {
3408 crate::Func::call_unchecked_raw(
3409 &mut store,
3410 return_.as_non_null(),
3411 my_src.as_mut_slice().into(),
3412 UncaughtException::Trap,
3413 )?;
3414 }
3415
3416 let thread = store.0.current_guest_thread()?;
3417 let state = store.0.concurrent_state_mut()?;
3418 if sync_caller {
3419 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3420 if let ResultInfo::Stack { result_count } = &result_info {
3421 match result_count {
3422 0 => None,
3423 1 => Some(my_src[0]),
3424 _ => unreachable!(),
3425 }
3426 } else {
3427 None
3428 },
3429 );
3430 }
3431 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3432 }),
3433 ty: task_return_type,
3434 memory: NonNull::new(memory).map(SendSyncPtr::new),
3435 string_encoding,
3436 },
3437 Caller::Guest { thread: old_thread },
3438 None,
3439 self.runtime_instance(callee_instance),
3440 callee_async_typed,
3441 false,
3444 )?;
3445
3446 store.0.set_thread(guest_thread)?;
3449 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3450
3451 Ok(())
3452 }
3453
3454 unsafe fn call_callback<T>(
3459 self,
3460 mut store: StoreContextMut<T>,
3461 function: SendSyncPtr<VMFuncRef>,
3462 event: Event,
3463 handle: u32,
3464 ) -> Result<u32> {
3465 let (ordinal, result) = event.parts();
3466 let params = &mut [
3467 ValRaw::u32(ordinal),
3468 ValRaw::u32(handle),
3469 ValRaw::u32(result),
3470 ];
3471 unsafe {
3476 crate::Func::call_unchecked_raw(
3477 &mut store,
3478 function.as_non_null(),
3479 params.as_mut_slice().into(),
3480 UncaughtException::Trap,
3481 )?;
3482 }
3483 Ok(params[0].get_u32())
3484 }
3485
3486 unsafe fn start_call<T: 'static>(
3504 self,
3505 mut store: StoreContextMut<T>,
3506 callback: *mut VMFuncRef,
3507 post_return: *mut VMFuncRef,
3508 callee: NonNull<VMFuncRef>,
3509 param_count: u32,
3510 result_count: u32,
3511 flags: u32,
3512 storage: &mut [MaybeUninit<ValRaw>],
3513 ) -> Result<()> {
3514 let token = StoreToken::new(store.as_context_mut());
3515 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3516 let guest_thread = store.0.current_guest_thread()?;
3517 let state = store.0.concurrent_state_mut()?;
3518
3519 if !state.event_loop_running {
3524 bail!(
3525 "async function started during an original synchronous \
3526 entrypoint; please use an async-style entrypoint instead"
3527 );
3528 }
3529
3530 let callee = SendSyncPtr::new(callee);
3531 let param_count = usize::try_from(param_count)?;
3532 assert!(param_count <= MAX_FLAT_PARAMS);
3533 let result_count = usize::try_from(result_count)?;
3534 assert!(result_count <= MAX_FLAT_RESULTS);
3535
3536 let task = state.get_mut(guest_thread.task)?;
3537 let callee_async_typed = task.async_typed;
3538 let callee_instance = task.instance;
3539
3540 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3541
3542 if let Some(callback) = NonNull::new(callback) {
3543 let callback = SendSyncPtr::new(callback);
3547 task.callback = Some(Box::new(move |store, event, handle| {
3548 let store = token.as_context_mut(store);
3549 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3550 }));
3551 }
3552
3553 let Caller::Guest { thread: caller } = &task.caller else {
3554 bail_bug!("start_call unexpectedly invoked for host->guest call");
3557 };
3558 let caller = *caller;
3559 let caller_instance = state.get_mut(caller.task)?.instance;
3560
3561 unsafe {
3563 self.stage_call(
3564 store.as_context_mut(),
3565 guest_thread,
3566 callee,
3567 param_count,
3568 result_count,
3569 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3570 NonNull::new(callback).map(SendSyncPtr::new),
3571 NonNull::new(post_return).map(SendSyncPtr::new),
3572 false,
3573 )?;
3574 }
3575
3576 let old_do_not_suspend = if callee_async_typed {
3577 let state = store.0.instance_state(callee_instance).concurrent_state();
3584 let old_do_not_suspend = state.do_not_suspend;
3585 state.do_not_suspend = false;
3586 Some(old_do_not_suspend)
3587 } else {
3588 None
3589 };
3590
3591 let state = store.0.concurrent_state_mut()?;
3592
3593 let guest_waitable = Waitable::Guest(guest_thread.task);
3596 let old_set = guest_waitable.common(state)?.set;
3597 let set = state.get_mut(caller.thread)?.sync_call_set;
3598 guest_waitable.join(state, Some(set))?;
3599
3600 store.0.set_thread(CurrentThread::None)?;
3601
3602 let mut yielded = false;
3618 let (status, waitable) = loop {
3619 store.0.suspend(if yielded {
3620 SuspendReason::Waiting {
3621 set,
3622 thread: caller,
3623 }
3624 } else {
3625 yielded = true;
3626 SuspendReason::YieldingToSubtask { thread: caller }
3627 })?;
3628
3629 if let Some(old_do_not_suspend) = old_do_not_suspend {
3630 store
3631 .0
3632 .instance_state(callee_instance)
3633 .concurrent_state()
3634 .do_not_suspend = old_do_not_suspend;
3635 }
3636
3637 let state = store.0.concurrent_state_mut()?;
3638
3639 log::trace!("taking event for {:?}", guest_thread.task);
3640 let event = guest_waitable.take_event(state)?;
3641 let Some(Event::Subtask { status }) = event else {
3642 bail_bug!("subtasks should only get subtask events, got {event:?}")
3643 };
3644
3645 log::trace!("status {status:?} for {:?}", guest_thread.task);
3646
3647 if status == Status::Returned {
3648 break (status, None);
3650 } else if async_caller {
3651 let (state, counter) = store.0.instance_state_and_handle_counter(caller_instance);
3655 let handle = state
3656 .handle_table()
3657 .subtask_insert_guest(counter, guest_thread.task.rep())?;
3658 store
3659 .0
3660 .concurrent_state_mut()?
3661 .get_mut(guest_thread.task)?
3662 .common
3663 .handle = Some(handle);
3664 break (status, Some(handle));
3665 } else {
3666 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3670 }
3671 };
3672
3673 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3674
3675 store.0.set_thread(caller)?;
3677
3678 store
3679 .0
3680 .concurrent_state_mut()?
3681 .set_thread_running(caller.thread)?;
3682
3683 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3684
3685 if async_caller {
3686 let Some(slot) = storage.first_mut() else {
3687 bail_bug!("no storage for async call status");
3688 };
3689 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3690 } else {
3691 let state = store.0.concurrent_state_mut()?;
3694 let task = state.get_mut(guest_thread.task)?;
3695 if let Some(Some(result)) = task.sync_result.take()? {
3696 storage[0] = MaybeUninit::new(result);
3697 }
3698 }
3699
3700 if status == Status::Returned {
3701 store
3702 .0
3703 .concurrent_state_mut()?
3704 .decrement_task_ref_count(guest_thread.task)?;
3705 }
3706
3707 Ok(())
3708 }
3709
3710 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3726 self,
3727 mut store: StoreContextMut<'_, T>,
3728 host_task: EnteredHostTask,
3729 result_may_require_realloc: bool,
3730 future: impl Future<Output = Result<R>> + Send + 'static,
3731 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3732 + Send
3733 + 'static,
3734 ) -> Result<u32> {
3735 let token = StoreToken::new(store.as_context_mut());
3736
3737 let (join_handle, future) = JoinHandle::run(future);
3740 let mut future = Box::pin(future);
3741
3742 let poll = tls::set(store.0, || {
3747 future
3748 .as_mut()
3749 .poll(&mut Context::from_waker(&Waker::noop()))
3750 });
3751
3752 match poll {
3753 Poll::Ready(result) => {
3755 let result = result.transpose()?;
3756 let task = store.0.current_materialized_host_task()?;
3759 lower(store.as_context_mut(), result, true, task)?;
3760 return Ok(Status::Returned.pack(None));
3761 }
3762
3763 Poll::Pending => {}
3765 }
3766
3767 let Some(task) = store.0.materialize_host_task_id()? else {
3771 bail_bug!("current thread is not a host thread")
3772 };
3773 {
3774 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3775 assert!(matches!(state, HostTaskState::CalleeStarted));
3776 *state = HostTaskState::CalleeRunning(join_handle);
3777 }
3778
3779 let future = Box::pin(async move {
3787 let result = match run_with_host_task_set(task, future).await? {
3788 Some(result) => Some(result?),
3789 None => None,
3790 };
3791 let on_complete = move |store: &mut dyn VMStore| {
3792 let mut store = token.as_context_mut(store);
3796 let old = store.0.set_thread(task)?;
3797
3798 let status = if result.is_some() {
3799 Status::Returned
3800 } else {
3801 Status::ReturnCancelled
3802 };
3803
3804 lower(store.as_context_mut(), result, false, Some(task))?;
3805 let state = store.0.concurrent_state_mut()?;
3806 match &mut state.get_mut(task)?.state {
3807 pending @ HostTaskState::CalleeCancelling => {
3810 *pending = HostTaskState::CalleeDone { cancelled: true };
3811 }
3812
3813 other => *other = HostTaskState::CalleeDone { cancelled: false },
3815 }
3816 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3817
3818 store.0.set_thread(old)?;
3819 Ok(())
3820 };
3821
3822 tls::get(move |store| {
3823 if result_may_require_realloc {
3824 store
3829 .concurrent_state_mut()?
3830 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3831 on_complete,
3832 ))));
3833 Ok(())
3834 } else {
3835 on_complete(store)
3838 }
3839 })
3840 });
3841
3842 let caller = match host_task {
3845 Some(caller) => caller,
3846 None => bail_bug!("host task wasn't created but should have been"),
3847 };
3848 let state = store.0.concurrent_state_mut()?;
3849 state.push_future(future);
3850 let instance = state.get_mut(caller.task)?.instance;
3851 let (state, counter) = store.0.instance_state_and_handle_counter(instance);
3852 let handle = state
3853 .handle_table()
3854 .subtask_insert_host(counter, task.rep())?;
3855 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3856 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3857
3858 store.0.set_thread(caller)?;
3862 Ok(Status::Started.pack(Some(handle)))
3863 }
3864
3865 pub(crate) fn task_return(
3868 self,
3869 store: &mut dyn VMStore,
3870 ty: TypeTupleIndex,
3871 options: OptionsIndex,
3872 storage: &[ValRaw],
3873 ) -> Result<()> {
3874 let guest_thread = store.current_guest_thread()?;
3875 let state = store.concurrent_state_mut()?;
3876 if !state.get_mut(guest_thread.task)?.async_lifted {
3877 bail!(Trap::TaskReturnOrCancelSyncLifted);
3878 }
3879 let lift = state
3880 .get_mut(guest_thread.task)?
3881 .lift_result
3882 .take()
3883 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3884 if !state.get_mut(guest_thread.task)?.result.is_none() {
3885 bail_bug!("task result unexpectedly already set");
3886 }
3887
3888 let CanonicalOptions {
3889 string_encoding,
3890 data_model,
3891 ..
3892 } = &self.id().get(store).component().env_component().options[options];
3893
3894 let invalid = ty != lift.ty
3895 || string_encoding != &lift.string_encoding
3896 || match data_model {
3897 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3898 Some(memory) => {
3899 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3900 let actual = self.id().get(store).runtime_memory(memory);
3901 expected != actual.as_ptr()
3902 }
3903 None => false,
3906 },
3907 CanonicalOptionsDataModel::Gc { .. } => true,
3909 };
3910
3911 if invalid {
3912 bail!(Trap::TaskReturnInvalid);
3913 }
3914
3915 log::trace!("task.return for {guest_thread:?}");
3916
3917 let result = (lift.lift)(store, storage)?;
3918 self.task_complete(store, guest_thread.task, result, Status::Returned)
3919 }
3920
3921 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3923 let guest_thread = store.current_guest_thread()?;
3924 let state = store.concurrent_state_mut()?;
3925 let task = state.get_mut(guest_thread.task)?;
3926 if !task.async_lifted {
3927 bail!(Trap::TaskReturnOrCancelSyncLifted);
3928 }
3929 if !task.cancel_request_delivered {
3930 bail!(Trap::TaskCancelNotCancelled);
3931 }
3932 _ = task
3933 .lift_result
3934 .take()
3935 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3936
3937 if !task.result.is_none() {
3938 bail_bug!("task result should not bet set yet");
3939 }
3940
3941 log::trace!("task.cancel for {guest_thread:?}");
3942
3943 self.task_complete(
3944 store,
3945 guest_thread.task,
3946 Box::new(DummyResult),
3947 Status::ReturnCancelled,
3948 )
3949 }
3950
3951 fn task_complete(
3957 self,
3958 store: &mut StoreOpaque,
3959 guest_task: TableId<GuestTask>,
3960 result: Box<dyn Any + Send + Sync>,
3961 status: Status,
3962 ) -> Result<()> {
3963 store
3964 .component_resource_tables(Some(self))?
3965 .validate_scope_exit()?;
3966
3967 let state = store.concurrent_state_mut()?;
3968 let task = state.get_mut(guest_task)?;
3969
3970 task.event = None;
3974
3975 if let Caller::Host { tx, .. } = &mut task.caller {
3976 if let Some(tx) = tx.take() {
3977 _ = tx.send(result);
3978 }
3979 } else {
3980 task.result = Some(result);
3981 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3982 }
3983
3984 Ok(())
3985 }
3986
3987 pub(crate) fn waitable_set_new(
3989 self,
3990 store: &mut StoreOpaque,
3991 caller_instance: RuntimeComponentInstanceIndex,
3992 ) -> Result<u32> {
3993 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3994 let (state, counter) =
3995 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3996 let handle = state
3997 .handle_table()
3998 .waitable_set_insert(counter, set.rep())?;
3999 log::trace!("new waitable set {set:?} (handle {handle})");
4000 Ok(handle)
4001 }
4002
4003 pub(crate) fn waitable_set_drop(
4005 self,
4006 store: &mut StoreOpaque,
4007 caller_instance: RuntimeComponentInstanceIndex,
4008 set: u32,
4009 ) -> Result<()> {
4010 let rep = store
4011 .instance_state(self.runtime_instance(caller_instance))
4012 .handle_table()
4013 .waitable_set_remove(set)?;
4014
4015 log::trace!("drop waitable set {rep} (handle {set})");
4016
4017 let set = store
4021 .concurrent_state_mut()?
4022 .get_mut(TableId::<WaitableSet>::new(rep))?;
4023 if set.num_waiting > 0 {
4024 bail!(Trap::WaitableSetDropHasWaiters);
4025 }
4026
4027 store
4028 .concurrent_state_mut()?
4029 .delete(TableId::<WaitableSet>::new(rep))?;
4030
4031 Ok(())
4032 }
4033
4034 pub(crate) fn waitable_join(
4036 self,
4037 store: &mut StoreOpaque,
4038 caller_instance: RuntimeComponentInstanceIndex,
4039 waitable_handle: u32,
4040 set_handle: u32,
4041 ) -> Result<()> {
4042 let mut instance = self.id().get_mut(store);
4043 let waitable =
4044 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4045
4046 let set = if set_handle == 0 {
4047 None
4048 } else {
4049 let set = instance.instance_states().0[caller_instance]
4050 .handle_table()
4051 .waitable_set_rep(set_handle)?;
4052
4053 Some(TableId::<WaitableSet>::new(set))
4054 };
4055
4056 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4057
4058 log::trace!(
4059 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4060 );
4061
4062 waitable.join(store.concurrent_state_mut()?, set)
4063 }
4064
4065 pub(crate) fn subtask_drop(
4067 self,
4068 store: &mut StoreOpaque,
4069 caller_instance: RuntimeComponentInstanceIndex,
4070 task_id: u32,
4071 ) -> Result<()> {
4072 self.waitable_join(store, caller_instance, task_id, 0)?;
4073
4074 let (rep, is_host) = store
4075 .instance_state(self.runtime_instance(caller_instance))
4076 .handle_table()
4077 .subtask_remove(task_id)?;
4078
4079 let concurrent_state = store.concurrent_state_mut()?;
4080 let waitable = if is_host {
4081 let id = TableId::<HostTask>::new(rep);
4082 let task = concurrent_state.get_mut(id)?;
4083 match &task.state {
4084 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4085 bail!(Trap::SubtaskDropNotResolved)
4086 }
4087 HostTaskState::CalleeDone { .. } => {}
4088 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4089 bail_bug!("invalid state for callee in `subtask.drop`")
4090 }
4091 }
4092
4093 Waitable::Host(id)
4094 } else {
4095 let id = TableId::<GuestTask>::new(rep);
4096 let task = concurrent_state.get_mut(id)?;
4097 if task.lift_result.is_some() {
4098 bail!(Trap::SubtaskDropNotResolved);
4099 }
4100 Waitable::Guest(id)
4101 };
4102
4103 waitable.common(concurrent_state)?.handle = None;
4104
4105 if waitable.take_event(concurrent_state)?.is_some() {
4108 bail!(Trap::SubtaskDropNotResolved);
4109 }
4110
4111 waitable.delete_from(concurrent_state)?;
4112
4113 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4114 Ok(())
4115 }
4116
4117 pub(crate) fn waitable_set_wait(
4119 self,
4120 store: &mut StoreOpaque,
4121 options: OptionsIndex,
4122 set: u32,
4123 payload: u32,
4124 ) -> Result<u32> {
4125 let &CanonicalOptions {
4126 instance: caller_instance,
4127 ..
4128 } = &self.id().get(store).component().env_component().options[options];
4129 let caller = self.runtime_instance(caller_instance);
4130 let rep = store
4131 .instance_state(self.runtime_instance(caller_instance))
4132 .handle_table()
4133 .waitable_set_rep(set)?;
4134
4135 self.waitable_check(
4136 store,
4137 caller,
4138 WaitableCheck::Wait,
4139 WaitableCheckParams {
4140 set: TableId::new(rep),
4141 options,
4142 payload,
4143 },
4144 )
4145 }
4146
4147 pub(crate) fn waitable_set_poll(
4149 self,
4150 store: &mut StoreOpaque,
4151 options: OptionsIndex,
4152 set: u32,
4153 payload: u32,
4154 ) -> Result<u32> {
4155 let &CanonicalOptions {
4156 instance: caller_instance,
4157 ..
4158 } = &self.id().get(store).component().env_component().options[options];
4159 let caller = self.runtime_instance(caller_instance);
4160 let rep = store
4161 .instance_state(caller)
4162 .handle_table()
4163 .waitable_set_rep(set)?;
4164
4165 self.waitable_check(
4166 store,
4167 caller,
4168 WaitableCheck::Poll,
4169 WaitableCheckParams {
4170 set: TableId::new(rep),
4171 options,
4172 payload,
4173 },
4174 )
4175 }
4176
4177 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4179 let thread_id = store.current_guest_thread()?.thread;
4180 match store
4181 .concurrent_state_mut()?
4182 .get_mut(thread_id)?
4183 .instance_rep
4184 {
4185 Some(r) => Ok(r),
4186 None => bail_bug!("thread should have instance_rep by now"),
4187 }
4188 }
4189
4190 pub(crate) fn thread_new_indirect<T: 'static>(
4192 self,
4193 mut store: StoreContextMut<T>,
4194 runtime_instance: RuntimeComponentInstanceIndex,
4195 start_func_ty: ModuleInternedTypeIndex,
4196 start_func_table_idx: RuntimeTableIndex,
4197 start_func_idx: u32,
4198 context: i32,
4199 ) -> Result<u32> {
4200 log::trace!("creating new thread");
4201
4202 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4203 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4204 else {
4205 bail_bug!("thread.new-indirect start function type should be registered");
4206 };
4207 let callee = instance
4208 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4209 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4210 if callee.type_index(store.0) != start_func_ty {
4211 bail!(Trap::ThreadNewIndirectInvalidType);
4212 }
4213
4214 let token = StoreToken::new(store.as_context_mut());
4215 let start_func = Box::new(
4216 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4217 let old_thread = store.set_thread(guest_thread)?;
4218 log::trace!(
4219 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4220 );
4221
4222 let mut store = token.as_context_mut(store);
4223 let mut params = [ValRaw::i32(context)];
4224 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4227
4228 store.0.set_thread(old_thread)?;
4229
4230 let runtime_instance = self.runtime_instance(runtime_instance);
4231
4232 store
4235 .0
4236 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4237
4238 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4239
4240 log::trace!("explicit thread {guest_thread:?} completed");
4241 let state = store.0.concurrent_state_mut()?;
4242 if let Some(t) = old_thread.guest() {
4243 state.set_thread_running(t.thread)?;
4244 }
4245 log::trace!("thread start: restored {old_thread:?} as current thread");
4246
4247 Ok(())
4248 },
4249 );
4250
4251 let current_thread = store.0.current_guest_thread()?;
4252 let state = store.0.concurrent_state_mut()?;
4253 let parent_task = current_thread.task;
4254
4255 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4256 let thread_id = state.push(new_thread)?;
4257 state.get_mut(parent_task)?.threads.insert(thread_id);
4258
4259 log::trace!("new thread with id {thread_id:?} created");
4260
4261 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4262 }
4263
4264 pub(crate) fn resume_thread(
4265 self,
4266 store: &mut StoreOpaque,
4267 runtime_instance: RuntimeComponentInstanceIndex,
4268 thread_idx: u32,
4269 how: ResumeThread,
4270 ) -> Result<bool> {
4271 let thread_id =
4272 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4273 let state = store.concurrent_state_mut()?;
4274 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4275
4276 if store.current_guest_thread()? == guest_thread {
4277 bail!(Trap::CannotResumeThread);
4278 }
4279
4280 let priority = match how {
4281 ResumeThread::Resume => Priority::Switch,
4282 ResumeThread::ResumeLater => Priority::Low,
4283
4284 ResumeThread::Promote => {
4287 let instance = self.runtime_instance(runtime_instance);
4288 let do_not_enter = store
4289 .instance_state(instance)
4290 .concurrent_state()
4291 .do_not_enter;
4292 return store.concurrent_state_mut()?.promote_work_item_matching(
4293 |item: &WorkItem| match item {
4294 WorkItem::ResumeThread { thread, .. }
4295 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4296
4297 WorkItem::GuestCall {
4298 call: GuestCall { thread, kind },
4299 ..
4300 } => {
4301 *thread == guest_thread
4302 && match kind {
4303 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4307
4308 GuestCallKind::StartExplicit(_) => true,
4311
4312 GuestCallKind::StartImplicit(_) => false,
4316 }
4317 }
4318
4319 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4320 },
4321 );
4322 }
4323 };
4324
4325 let state = store.concurrent_state_mut()?;
4326 let thread = state.get_mut(guest_thread.thread)?;
4327
4328 match &thread.state {
4331 GuestThreadState::NotStartedExplicit(_) => {}
4332 GuestThreadState::Suspended(_) => {
4333 log::trace!("resuming thread {thread_id:?} that was suspended");
4334 state.schedule_suspended_thread(
4335 guest_thread,
4336 self.runtime_instance(runtime_instance),
4337 priority,
4338 )?;
4339 return Ok(true);
4340 }
4341 _ => bail!(Trap::CannotResumeThread),
4342 }
4343 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4344 GuestThreadState::NotStartedExplicit(start_func) => {
4345 log::trace!("starting thread {guest_thread:?}");
4346 let guest_call = WorkItem::GuestCall {
4347 instance: self.runtime_instance(runtime_instance),
4348 call: GuestCall {
4349 thread: guest_thread,
4350 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4351 start_func(store, guest_thread)
4352 })),
4353 },
4354 };
4355 state.push_work_item(guest_call, priority)?;
4356 }
4357 other => {
4358 thread.state = other;
4359 bail_bug!("thread state checked to be resumable above");
4360 }
4361 }
4362 Ok(true)
4363 }
4364
4365 fn add_guest_thread_to_instance_table(
4366 self,
4367 thread_id: TableId<GuestThread>,
4368 store: &mut StoreOpaque,
4369 runtime_instance: RuntimeComponentInstanceIndex,
4370 ) -> Result<u32> {
4371 let (state, counter) =
4372 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4373 let guest_id = state
4374 .thread_handle_table()
4375 .guest_thread_insert(counter, thread_id.rep())?;
4376 store
4377 .concurrent_state_mut()?
4378 .get_mut(thread_id)?
4379 .instance_rep = Some(guest_id);
4380 Ok(guest_id)
4381 }
4382
4383 pub(crate) fn suspension_intrinsic(
4387 self,
4388 store: &mut StoreOpaque,
4389 caller: RuntimeComponentInstanceIndex,
4390 yielding: bool,
4391 to_thread: SuspensionTarget,
4392 ) -> Result<WaitResult> {
4393 let check_suspend = match to_thread {
4394 SuspensionTarget::Promote(thread) => {
4395 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4396 }
4397 SuspensionTarget::Resume(thread) => {
4398 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4399 bail_bug!(
4400 "`resume_thread` should only ever return false \
4401 when `ResumeThread::Promote` is passed to it"
4402 );
4403 }
4404 false
4405 }
4406 SuspensionTarget::None => true,
4407 };
4408
4409 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4410 return if yielding {
4411 Ok(WaitResult::Completed)
4412 } else {
4413 Err(Trap::CannotBlockSyncTask.into())
4414 };
4415 }
4416
4417 let guest_thread = store.current_guest_thread()?;
4418
4419 let reason = if yielding {
4420 SuspendReason::Yielding {
4421 thread: guest_thread,
4422 }
4423 } else {
4424 SuspendReason::ExplicitlySuspending {
4425 thread: guest_thread,
4426 }
4427 };
4428
4429 store.suspend(reason)?;
4430
4431 Ok(WaitResult::Completed)
4432 }
4433
4434 fn waitable_check(
4436 self,
4437 store: &mut StoreOpaque,
4438 caller: RuntimeInstance,
4439 check: WaitableCheck,
4440 params: WaitableCheckParams,
4441 ) -> Result<u32> {
4442 let guest_thread = store.current_guest_thread()?;
4443
4444 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4445
4446 match &check {
4449 WaitableCheck::Wait => {
4450 let set = params.set;
4451
4452 loop {
4457 let state = store.concurrent_state_mut()?;
4458 let task = state.get_mut(guest_thread.task)?;
4459 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4460 || !state.get_mut(set)?.ready.is_empty()
4461 {
4462 break;
4463 }
4464
4465 store.switch_or_trap_if_may_not_suspend(caller)?;
4466
4467 store.suspend(SuspendReason::Waiting {
4468 set,
4469 thread: guest_thread,
4470 })?;
4471 }
4472 }
4473 WaitableCheck::Poll => {}
4474 }
4475
4476 log::trace!(
4477 "waitable check for {guest_thread:?}; set {:?}, part two",
4478 params.set
4479 );
4480
4481 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4483
4484 let (ordinal, handle, result) = match &check {
4485 WaitableCheck::Wait => {
4486 let (event, waitable) = match event {
4487 Some(p) => p,
4488 None => bail_bug!("event expected to be present"),
4489 };
4490 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4491 let (ordinal, result) = event.parts();
4492 (ordinal, handle, result)
4493 }
4494 WaitableCheck::Poll => {
4495 if let Some((event, waitable)) = event {
4496 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4497 let (ordinal, result) = event.parts();
4498 (ordinal, handle, result)
4499 } else {
4500 log::trace!(
4501 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4502 guest_thread.task,
4503 params.set
4504 );
4505 let (ordinal, result) = Event::None.parts();
4506 (ordinal, 0, result)
4507 }
4508 }
4509 };
4510 let memory = self.options_memory_mut(store, params.options);
4511 let ptr = crate::component::func::validate_inbounds_dynamic(
4512 &CanonicalAbiInfo::POINTER_PAIR,
4513 memory,
4514 &ValRaw::u32(params.payload),
4515 )?;
4516 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4517 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4518 Ok(ordinal)
4519 }
4520
4521 pub(crate) fn subtask_cancel(
4523 self,
4524 store: &mut StoreOpaque,
4525 caller_instance: RuntimeComponentInstanceIndex,
4526 async_: bool,
4527 task_id: u32,
4528 ) -> Result<u32> {
4529 let (rep, is_host) = store
4530 .instance_state(self.runtime_instance(caller_instance))
4531 .handle_table()
4532 .subtask_rep(task_id)?;
4533 let waitable = if is_host {
4534 Waitable::Host(TableId::<HostTask>::new(rep))
4535 } else {
4536 Waitable::Guest(TableId::<GuestTask>::new(rep))
4537 };
4538 let concurrent_state = store.concurrent_state_mut()?;
4539
4540 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4541
4542 waitable.trap_if_in_waitable_set(concurrent_state)?;
4543
4544 let needs_block;
4545 if let Waitable::Host(host_task) = waitable {
4546 let state = &mut concurrent_state.get_mut(host_task)?.state;
4547 match state {
4548 HostTaskState::CalleeRunning(handle) => {
4555 handle.abort();
4556 *state = HostTaskState::CalleeCancelling;
4557 needs_block = true;
4558 }
4559
4560 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4563 bail!(Trap::SubtaskCancelAfterTerminal);
4564 }
4565 HostTaskState::CalleeDone { cancelled: false } => {
4566 *state = HostTaskState::CalleeDone { cancelled: true };
4569 needs_block = false;
4570 }
4571
4572 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4575 bail_bug!("invalid states for host callee")
4576 }
4577 }
4578 } else {
4579 let guest_task = TableId::<GuestTask>::new(rep);
4580 let task = concurrent_state.get_mut(guest_task)?;
4581
4582 if task.cancel_requested {
4585 bail!(Trap::SubtaskCancelAfterTerminal);
4586 }
4587
4588 if !task.already_lowered_parameters() {
4589 store.cancel_guest_subtask_without_lowered_parameters(
4590 self.runtime_instance(caller_instance),
4591 guest_task,
4592 )?;
4593 return Ok(Status::StartCancelled as u32);
4594 } else if !task.returned_or_cancelled() {
4595 task.cancel_requested = true;
4602 task.event = Some(Event::Cancelled);
4603 let runtime_instance = task.instance;
4604 for thread in task.threads.clone() {
4605 let thread = QualifiedThreadId {
4606 task: guest_task,
4607 thread,
4608 };
4609 let concurrent_state = store.concurrent_state_mut()?;
4610 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4611
4612 let yield_ = |store: &mut StoreOpaque| {
4613 let state = store.instance_state(runtime_instance).concurrent_state();
4618 let old_do_not_suspend = state.do_not_suspend;
4619 state.do_not_suspend = false;
4620
4621 let caller = store.current_guest_thread()?;
4622
4623 let state = store.concurrent_state_mut()?;
4628 let set = state.get_mut(caller.thread)?.sync_call_set;
4629 waitable.join(state, Some(set))?;
4630
4631 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4632
4633 let state = store.concurrent_state_mut()?;
4634 waitable.join(state, None)?;
4635
4636 store
4637 .instance_state(runtime_instance)
4638 .concurrent_state()
4639 .do_not_suspend = old_do_not_suspend;
4640
4641 Ok::<(), crate::Error>(())
4642 };
4643
4644 match thread_mut.wake_on_cancel.take() {
4645 WakeOnCancel::Waiting(set) => {
4646 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4648 let item = WorkItem::GuestCall {
4649 instance: runtime_instance,
4650 call: GuestCall {
4651 thread,
4652 kind: GuestCallKind::DeliverEvent {
4653 instance,
4654 set: Some(set),
4655 },
4656 },
4657 };
4658 concurrent_state.set_switch_item(item)?;
4659
4660 yield_(store)?;
4661
4662 break;
4663 }
4664 WakeOnCancel::Yielding => {
4665 if concurrent_state.promote_thread_work_item(thread)? {
4666 yield_(store)?;
4667 break;
4668 } else if store
4669 .instance_state(runtime_instance)
4670 .concurrent_state()
4671 .pending
4672 .contains_key(&thread)
4673 {
4674 store
4684 .concurrent_state_mut()?
4685 .get_mut(thread.thread)?
4686 .wake_on_cancel = WakeOnCancel::Yielding;
4687 } else {
4688 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4689 }
4690 }
4691 WakeOnCancel::None => {}
4692 }
4693 }
4694
4695 needs_block = !store
4698 .concurrent_state_mut()?
4699 .get_mut(guest_task)?
4700 .returned_or_cancelled()
4701 } else {
4702 needs_block = false;
4703 }
4704 };
4705
4706 if needs_block {
4710 if async_ {
4711 return Ok(BLOCKED);
4712 }
4713
4714 let old_next_switch_item = if is_host {
4724 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4725 } else {
4726 None
4727 };
4728
4729 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4732
4733 if let Some(item) = old_next_switch_item {
4734 let state = store.concurrent_state_mut()?;
4735 state.restore_next_switch_item(item)?;
4736 }
4737
4738 }
4740
4741 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4742 if let Some(Event::Subtask {
4743 status: status @ (Status::Returned | Status::ReturnCancelled),
4744 }) = event
4745 {
4746 Ok(status as u32)
4747 } else {
4748 bail!(Trap::SubtaskCancelAfterTerminal);
4749 }
4750 }
4751}
4752
4753pub trait VMComponentAsyncStore {
4761 unsafe fn prepare_call(
4767 &mut self,
4768 instance: Instance,
4769 memory: *mut VMMemoryDefinition,
4770 start: NonNull<VMFuncRef>,
4771 return_: NonNull<VMFuncRef>,
4772 caller_instance: RuntimeComponentInstanceIndex,
4773 callee_instance: RuntimeComponentInstanceIndex,
4774 task_return_type: TypeTupleIndex,
4775 callee_async: bool,
4776 string_encoding: StringEncoding,
4777 result_count: u32,
4778 storage: *mut ValRaw,
4779 storage_len: usize,
4780 ) -> Result<()>;
4781
4782 unsafe fn start_call(
4785 &mut self,
4786 instance: Instance,
4787 callback: *mut VMFuncRef,
4788 post_return: *mut VMFuncRef,
4789 callee: NonNull<VMFuncRef>,
4790 param_count: u32,
4791 result_count: u32,
4792 flags: u32,
4793 storage: *mut MaybeUninit<ValRaw>,
4794 storage_len: usize,
4795 ) -> Result<()>;
4796
4797 fn future_write(
4799 &mut self,
4800 instance: Instance,
4801 caller: RuntimeComponentInstanceIndex,
4802 ty: TypeFutureTableIndex,
4803 options: OptionsIndex,
4804 future: u32,
4805 address: u32,
4806 ) -> Result<u32>;
4807
4808 fn future_read(
4810 &mut self,
4811 instance: Instance,
4812 caller: RuntimeComponentInstanceIndex,
4813 ty: TypeFutureTableIndex,
4814 options: OptionsIndex,
4815 future: u32,
4816 address: u32,
4817 ) -> Result<u32>;
4818
4819 fn future_drop_writable(
4821 &mut self,
4822 instance: Instance,
4823 ty: TypeFutureTableIndex,
4824 writer: u32,
4825 ) -> Result<()>;
4826
4827 fn stream_write(
4829 &mut self,
4830 instance: Instance,
4831 caller: RuntimeComponentInstanceIndex,
4832 ty: TypeStreamTableIndex,
4833 options: OptionsIndex,
4834 stream: u32,
4835 address: u32,
4836 count: u32,
4837 ) -> Result<u32>;
4838
4839 fn stream_read(
4841 &mut self,
4842 instance: Instance,
4843 caller: RuntimeComponentInstanceIndex,
4844 ty: TypeStreamTableIndex,
4845 options: OptionsIndex,
4846 stream: u32,
4847 address: u32,
4848 count: u32,
4849 ) -> Result<u32>;
4850
4851 fn flat_stream_write(
4854 &mut self,
4855 instance: Instance,
4856 caller: RuntimeComponentInstanceIndex,
4857 ty: TypeStreamTableIndex,
4858 options: OptionsIndex,
4859 payload_size: u32,
4860 payload_align: u32,
4861 stream: u32,
4862 address: u32,
4863 count: u32,
4864 ) -> Result<u32>;
4865
4866 fn flat_stream_read(
4869 &mut self,
4870 instance: Instance,
4871 caller: RuntimeComponentInstanceIndex,
4872 ty: TypeStreamTableIndex,
4873 options: OptionsIndex,
4874 payload_size: u32,
4875 payload_align: u32,
4876 stream: u32,
4877 address: u32,
4878 count: u32,
4879 ) -> Result<u32>;
4880
4881 fn stream_drop_writable(
4883 &mut self,
4884 instance: Instance,
4885 ty: TypeStreamTableIndex,
4886 writer: u32,
4887 ) -> Result<()>;
4888
4889 fn error_context_debug_message(
4891 &mut self,
4892 instance: Instance,
4893 ty: TypeComponentLocalErrorContextTableIndex,
4894 options: OptionsIndex,
4895 err_ctx_handle: u32,
4896 debug_msg_address: u32,
4897 ) -> Result<()>;
4898
4899 fn thread_new_indirect(
4901 &mut self,
4902 instance: Instance,
4903 caller: RuntimeComponentInstanceIndex,
4904 func_ty_idx: ModuleInternedTypeIndex,
4905 start_func_table_idx: RuntimeTableIndex,
4906 start_func_idx: u32,
4907 context: i32,
4908 ) -> Result<u32>;
4909}
4910
4911impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4913 unsafe fn prepare_call(
4914 &mut self,
4915 instance: Instance,
4916 memory: *mut VMMemoryDefinition,
4917 start: NonNull<VMFuncRef>,
4918 return_: NonNull<VMFuncRef>,
4919 caller_instance: RuntimeComponentInstanceIndex,
4920 callee_instance: RuntimeComponentInstanceIndex,
4921 task_return_type: TypeTupleIndex,
4922 callee_async: bool,
4923 string_encoding: StringEncoding,
4924 result_count_or_max_if_async: u32,
4925 storage: *mut ValRaw,
4926 storage_len: usize,
4927 ) -> Result<()> {
4928 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4932
4933 unsafe {
4934 instance.prepare_call(
4935 StoreContextMut(self),
4936 start,
4937 return_,
4938 caller_instance,
4939 callee_instance,
4940 task_return_type,
4941 callee_async,
4942 memory,
4943 string_encoding,
4944 match result_count_or_max_if_async {
4945 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4946 params,
4947 has_result: false,
4948 },
4949 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4950 params,
4951 has_result: true,
4952 },
4953 result_count => CallerInfo::Sync {
4954 params,
4955 result_count,
4956 },
4957 },
4958 )
4959 }
4960 }
4961
4962 unsafe fn start_call(
4963 &mut self,
4964 instance: Instance,
4965 callback: *mut VMFuncRef,
4966 post_return: *mut VMFuncRef,
4967 callee: NonNull<VMFuncRef>,
4968 param_count: u32,
4969 result_count: u32,
4970 flags: u32,
4971 storage: *mut MaybeUninit<ValRaw>,
4972 storage_len: usize,
4973 ) -> Result<()> {
4974 unsafe {
4975 instance.start_call(
4976 StoreContextMut(self),
4977 callback,
4978 post_return,
4979 callee,
4980 param_count,
4981 result_count,
4982 flags,
4983 core::slice::from_raw_parts_mut(storage, storage_len),
4987 )
4988 }
4989 }
4990
4991 fn future_write(
4992 &mut self,
4993 instance: Instance,
4994 caller: RuntimeComponentInstanceIndex,
4995 ty: TypeFutureTableIndex,
4996 options: OptionsIndex,
4997 future: u32,
4998 address: u32,
4999 ) -> Result<u32> {
5000 instance
5001 .guest_write(
5002 StoreContextMut(self),
5003 caller,
5004 TransmitIndex::Future(ty),
5005 options,
5006 None,
5007 future,
5008 address,
5009 1,
5010 )
5011 .map(|result| result.encode())
5012 }
5013
5014 fn future_read(
5015 &mut self,
5016 instance: Instance,
5017 caller: RuntimeComponentInstanceIndex,
5018 ty: TypeFutureTableIndex,
5019 options: OptionsIndex,
5020 future: u32,
5021 address: u32,
5022 ) -> Result<u32> {
5023 instance
5024 .guest_read(
5025 StoreContextMut(self),
5026 caller,
5027 TransmitIndex::Future(ty),
5028 options,
5029 None,
5030 future,
5031 address,
5032 1,
5033 )
5034 .map(|result| result.encode())
5035 }
5036
5037 fn stream_write(
5038 &mut self,
5039 instance: Instance,
5040 caller: RuntimeComponentInstanceIndex,
5041 ty: TypeStreamTableIndex,
5042 options: OptionsIndex,
5043 stream: u32,
5044 address: u32,
5045 count: u32,
5046 ) -> Result<u32> {
5047 instance
5048 .guest_write(
5049 StoreContextMut(self),
5050 caller,
5051 TransmitIndex::Stream(ty),
5052 options,
5053 None,
5054 stream,
5055 address,
5056 count,
5057 )
5058 .map(|result| result.encode())
5059 }
5060
5061 fn stream_read(
5062 &mut self,
5063 instance: Instance,
5064 caller: RuntimeComponentInstanceIndex,
5065 ty: TypeStreamTableIndex,
5066 options: OptionsIndex,
5067 stream: u32,
5068 address: u32,
5069 count: u32,
5070 ) -> Result<u32> {
5071 instance
5072 .guest_read(
5073 StoreContextMut(self),
5074 caller,
5075 TransmitIndex::Stream(ty),
5076 options,
5077 None,
5078 stream,
5079 address,
5080 count,
5081 )
5082 .map(|result| result.encode())
5083 }
5084
5085 fn future_drop_writable(
5086 &mut self,
5087 instance: Instance,
5088 ty: TypeFutureTableIndex,
5089 writer: u32,
5090 ) -> Result<()> {
5091 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5092 }
5093
5094 fn flat_stream_write(
5095 &mut self,
5096 instance: Instance,
5097 caller: RuntimeComponentInstanceIndex,
5098 ty: TypeStreamTableIndex,
5099 options: OptionsIndex,
5100 payload_size: u32,
5101 payload_align: u32,
5102 stream: u32,
5103 address: u32,
5104 count: u32,
5105 ) -> Result<u32> {
5106 instance
5107 .guest_write(
5108 StoreContextMut(self),
5109 caller,
5110 TransmitIndex::Stream(ty),
5111 options,
5112 Some(FlatAbi {
5113 size: payload_size,
5114 align: payload_align,
5115 }),
5116 stream,
5117 address,
5118 count,
5119 )
5120 .map(|result| result.encode())
5121 }
5122
5123 fn flat_stream_read(
5124 &mut self,
5125 instance: Instance,
5126 caller: RuntimeComponentInstanceIndex,
5127 ty: TypeStreamTableIndex,
5128 options: OptionsIndex,
5129 payload_size: u32,
5130 payload_align: u32,
5131 stream: u32,
5132 address: u32,
5133 count: u32,
5134 ) -> Result<u32> {
5135 instance
5136 .guest_read(
5137 StoreContextMut(self),
5138 caller,
5139 TransmitIndex::Stream(ty),
5140 options,
5141 Some(FlatAbi {
5142 size: payload_size,
5143 align: payload_align,
5144 }),
5145 stream,
5146 address,
5147 count,
5148 )
5149 .map(|result| result.encode())
5150 }
5151
5152 fn stream_drop_writable(
5153 &mut self,
5154 instance: Instance,
5155 ty: TypeStreamTableIndex,
5156 writer: u32,
5157 ) -> Result<()> {
5158 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5159 }
5160
5161 fn error_context_debug_message(
5162 &mut self,
5163 instance: Instance,
5164 ty: TypeComponentLocalErrorContextTableIndex,
5165 options: OptionsIndex,
5166 err_ctx_handle: u32,
5167 debug_msg_address: u32,
5168 ) -> Result<()> {
5169 instance.error_context_debug_message(
5170 StoreContextMut(self),
5171 ty,
5172 options,
5173 err_ctx_handle,
5174 debug_msg_address,
5175 )
5176 }
5177
5178 fn thread_new_indirect(
5179 &mut self,
5180 instance: Instance,
5181 caller: RuntimeComponentInstanceIndex,
5182 func_ty_idx: ModuleInternedTypeIndex,
5183 start_func_table_idx: RuntimeTableIndex,
5184 start_func_idx: u32,
5185 context: i32,
5186 ) -> Result<u32> {
5187 instance.thread_new_indirect(
5188 StoreContextMut(self),
5189 caller,
5190 func_ty_idx,
5191 start_func_table_idx,
5192 start_func_idx,
5193 context,
5194 )
5195 }
5196}
5197
5198type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5199
5200async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5203where
5204 F: Future,
5205{
5206 let mut future = pin!(future);
5207 future::poll_fn(|cx| {
5208 let old_thread = match tls::get(|store| store.set_thread(task)) {
5209 Ok(thread) => thread,
5210 Err(error) => return Poll::Ready(Err(error)),
5211 };
5212 let result = future.as_mut().poll(cx);
5213 match tls::get(|store| store.set_thread(old_thread)) {
5214 Ok(_) => result.map(Ok),
5215 Err(error) => Poll::Ready(Err(error)),
5216 }
5217 })
5218 .await
5219}
5220
5221pub(crate) struct HostTask {
5225 common: WaitableCommon,
5226
5227 call_context: CallContext,
5230
5231 state: HostTaskState,
5232
5233 group: TaskGroupId,
5234}
5235
5236enum HostTaskState {
5237 CalleeStarted,
5242
5243 CalleeRunning(JoinHandle),
5248
5249 CalleeCancelling,
5253
5254 CalleeFinished(LiftedResult),
5258
5259 CalleeDone { cancelled: bool },
5262}
5263
5264impl HostTask {
5265 fn new(
5266 concurrent_state: &mut ConcurrentState,
5267 state: HostTaskState,
5268 caller: QualifiedThreadId,
5269 ) -> Result<Self> {
5270 let group = concurrent_state.get_mut(caller.task)?.group;
5271 concurrent_state.increment_group_ref_count(group)?;
5272
5273 Ok(Self {
5274 common: WaitableCommon::default(),
5275 call_context: CallContext::default(),
5276 state,
5277 group,
5278 })
5279 }
5280}
5281
5282impl TableDebug for HostTask {
5283 fn type_name() -> &'static str {
5284 "HostTask"
5285 }
5286}
5287
5288type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5289
5290enum Caller {
5292 Host {
5294 tx: Option<oneshot::Sender<LiftedResult>>,
5296 caller: Option<TableId<HostTask>>,
5300 },
5301 Guest {
5303 thread: QualifiedThreadId,
5305 },
5306}
5307
5308struct LiftResult {
5311 lift: RawLift,
5312 ty: TypeTupleIndex,
5313 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5314 string_encoding: StringEncoding,
5315}
5316
5317#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5322pub(crate) struct QualifiedThreadId {
5323 task: TableId<GuestTask>,
5324 thread: TableId<GuestThread>,
5325}
5326
5327impl QualifiedThreadId {
5328 fn qualify(
5329 state: &mut ConcurrentState,
5330 thread: TableId<GuestThread>,
5331 ) -> Result<QualifiedThreadId> {
5332 Ok(QualifiedThreadId {
5333 task: state.get_mut(thread)?.parent_task,
5334 thread,
5335 })
5336 }
5337}
5338
5339impl fmt::Debug for QualifiedThreadId {
5340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5341 f.debug_tuple("QualifiedThreadId")
5342 .field(&self.task.rep())
5343 .field(&self.thread.rep())
5344 .finish()
5345 }
5346}
5347
5348enum GuestThreadState {
5349 NotStartedImplicit,
5350 NotStartedExplicit(
5351 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5352 ),
5353 Running,
5354 Suspended(StoreFiber<'static>),
5355 Ready {
5356 fiber: StoreFiber<'static>,
5357 },
5358 Completed,
5359}
5360
5361impl GuestThreadState {
5362 fn check_no_fiber(&self) -> Result<()> {
5363 match self {
5364 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5365 Self::NotStartedImplicit
5366 | Self::NotStartedExplicit(_)
5367 | Self::Running
5368 | Self::Completed => Ok(()),
5369 }
5370 }
5371}
5372
5373impl fmt::Debug for GuestThreadState {
5374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5375 match self {
5376 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5377 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5378 Self::Running => f.debug_tuple("Running").finish(),
5379 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5380 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5381 Self::Completed => f.debug_tuple("Completed").finish(),
5382 }
5383 }
5384}
5385
5386#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5387enum WakeOnCancel {
5388 None,
5389 Waiting(TableId<WaitableSet>),
5390 Yielding,
5391}
5392
5393impl WakeOnCancel {
5394 fn is_none(self) -> bool {
5395 matches!(self, WakeOnCancel::None)
5396 }
5397
5398 fn replace(&mut self, other: WakeOnCancel) -> Self {
5399 let old = *self;
5400 *self = other;
5401 old
5402 }
5403
5404 fn take(&mut self) -> Self {
5405 self.replace(WakeOnCancel::None)
5406 }
5407}
5408
5409pub struct GuestThread {
5410 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5413 parent_task: TableId<GuestTask>,
5415 wake_on_cancel: WakeOnCancel,
5418 state: GuestThreadState,
5420 instance_rep: Option<u32>,
5423 sync_call_set: TableId<WaitableSet>,
5425 old_do_not_suspend: Option<bool>,
5428}
5429
5430impl GuestThread {
5431 fn from_instance(
5434 state: Pin<&mut ComponentInstance>,
5435 caller_instance: RuntimeComponentInstanceIndex,
5436 guest_thread: u32,
5437 ) -> Result<TableId<Self>> {
5438 let rep = state.instance_states().0[caller_instance]
5439 .thread_handle_table()
5440 .guest_thread_rep(guest_thread)?;
5441 Ok(TableId::new(rep))
5442 }
5443
5444 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5445 let sync_call_set = state.push(WaitableSet {
5446 is_sync_call_set: true,
5447 ..WaitableSet::default()
5448 })?;
5449 Ok(Self {
5450 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5451 parent_task,
5452 wake_on_cancel: WakeOnCancel::None,
5453 state: GuestThreadState::NotStartedImplicit,
5454 instance_rep: None,
5455 sync_call_set,
5456 old_do_not_suspend: None,
5457 })
5458 }
5459
5460 fn new_explicit(
5461 state: &mut ConcurrentState,
5462 parent_task: TableId<GuestTask>,
5463 start_func: Box<
5464 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5465 >,
5466 ) -> Result<Self> {
5467 state.increment_task_ref_count(parent_task)?;
5468 let sync_call_set = state.push(WaitableSet {
5469 is_sync_call_set: true,
5470 ..WaitableSet::default()
5471 })?;
5472 Ok(Self {
5473 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5474 parent_task,
5475 wake_on_cancel: WakeOnCancel::None,
5476 state: GuestThreadState::NotStartedExplicit(start_func),
5477 instance_rep: None,
5478 sync_call_set,
5479 old_do_not_suspend: None,
5480 })
5481 }
5482}
5483
5484impl TableDebug for GuestThread {
5485 fn type_name() -> &'static str {
5486 "GuestThread"
5487 }
5488}
5489
5490enum SyncResult {
5491 NotProduced,
5492 Produced(Option<ValRaw>),
5493 Taken,
5494}
5495
5496impl SyncResult {
5497 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5498 Ok(match mem::replace(self, SyncResult::Taken) {
5499 SyncResult::NotProduced => None,
5500 SyncResult::Produced(val) => Some(val),
5501 SyncResult::Taken => {
5502 bail_bug!("attempted to take a synchronous result that was already taken")
5503 }
5504 })
5505 }
5506}
5507
5508pub(crate) struct GuestTask {
5510 common: WaitableCommon,
5512 lower_params: Option<RawLower>,
5514 lift_result: Option<LiftResult>,
5516 result: Option<LiftedResult>,
5519 callback: Option<CallbackFn>,
5522 caller: Caller,
5524 call_context: CallContext,
5529 sync_result: SyncResult,
5532 cancel_request_delivered: bool,
5536 cancel_requested: bool,
5541 starting_sent: bool,
5544 instance: RuntimeInstance,
5551 event: Option<Event>,
5553 exited: bool,
5555 threads: HashSet<TableId<GuestThread>>,
5557 async_typed: bool,
5560 async_lifted: bool,
5563 decremented_interesting_task_count: bool,
5566 group: TaskGroupId,
5568 ref_count: usize,
5573}
5574
5575impl GuestTask {
5576 fn already_lowered_parameters(&self) -> bool {
5577 self.lower_params.is_none()
5579 }
5580
5581 fn returned_or_cancelled(&self) -> bool {
5582 self.lift_result.is_none()
5584 }
5585
5586 fn new(
5587 state: &mut ConcurrentState,
5588 lower_params: RawLower,
5589 lift_result: LiftResult,
5590 caller: Caller,
5591 callback: Option<CallbackFn>,
5592 instance: RuntimeInstance,
5593 async_typed: bool,
5594 async_lifted: bool,
5595 ) -> Result<QualifiedThreadId> {
5596 let group = match caller {
5597 Caller::Guest { thread } => {
5598 let group = state.get_mut(thread.task)?.group;
5599 state.increment_group_ref_count(group)?;
5600 group
5601 }
5602 Caller::Host { .. } => state.make_task_group()?,
5603 };
5604
5605 let task = state.push(Self {
5606 common: WaitableCommon::default(),
5607 lower_params: Some(lower_params),
5608 lift_result: Some(lift_result),
5609 result: None,
5610 callback,
5611 caller,
5612 call_context: CallContext::default(),
5613 sync_result: SyncResult::NotProduced,
5614 cancel_request_delivered: false,
5615 cancel_requested: false,
5616 starting_sent: false,
5617 instance,
5618 event: None,
5619 exited: false,
5620 threads: HashSet::new(),
5621 async_typed,
5622 async_lifted,
5623 decremented_interesting_task_count: false,
5624 group,
5625 ref_count: 2,
5628 })?;
5629 let new_thread = GuestThread::new_implicit(state, task)?;
5630 let thread = state.push(new_thread)?;
5631 state.get_mut(task)?.threads.insert(thread);
5632 state.interesting_tasks += 1;
5633 let thread = QualifiedThreadId { task, thread };
5634 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5635 Ok(thread)
5636 }
5637}
5638
5639impl TableDebug for GuestTask {
5640 fn type_name() -> &'static str {
5641 "GuestTask"
5642 }
5643}
5644
5645#[derive(Default)]
5647struct WaitableCommon {
5648 event: Option<Event>,
5650 set: Option<TableId<WaitableSet>>,
5652 handle: Option<u32>,
5654}
5655
5656#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5658enum Waitable {
5659 Host(TableId<HostTask>),
5661 Guest(TableId<GuestTask>),
5663 Transmit(TableId<TransmitHandle>),
5665}
5666
5667impl Waitable {
5668 fn from_instance(
5671 state: Pin<&mut ComponentInstance>,
5672 caller_instance: RuntimeComponentInstanceIndex,
5673 waitable: u32,
5674 ) -> Result<Self> {
5675 use crate::runtime::vm::component::Waitable;
5676
5677 let (waitable, kind) = state.instance_states().0[caller_instance]
5678 .handle_table()
5679 .waitable_rep(waitable)?;
5680
5681 Ok(match kind {
5682 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5683 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5684 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5685 })
5686 }
5687
5688 fn rep(&self) -> u32 {
5690 match self {
5691 Self::Host(id) => id.rep(),
5692 Self::Guest(id) => id.rep(),
5693 Self::Transmit(id) => id.rep(),
5694 }
5695 }
5696
5697 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5701 log::trace!("waitable {self:?} join set {set:?}");
5702
5703 let old = mem::replace(&mut self.common(state)?.set, set);
5704
5705 if let Some(old) = old {
5706 match *self {
5707 Waitable::Host(id) => state.remove_child(id, old),
5708 Waitable::Guest(id) => state.remove_child(id, old),
5709 Waitable::Transmit(id) => state.remove_child(id, old),
5710 }?;
5711
5712 state.get_mut(old)?.ready.remove(self);
5713 }
5714
5715 if let Some(set) = set {
5716 match *self {
5717 Waitable::Host(id) => state.add_child(id, set),
5718 Waitable::Guest(id) => state.add_child(id, set),
5719 Waitable::Transmit(id) => state.add_child(id, set),
5720 }?;
5721
5722 if self.common(state)?.event.is_some() {
5723 self.mark_ready(state)?;
5724 }
5725 }
5726
5727 Ok(())
5728 }
5729
5730 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5732 Ok(match self {
5733 Self::Host(id) => &mut state.get_mut(*id)?.common,
5734 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5735 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5736 })
5737 }
5738
5739 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5745 if self.common(state)?.set.is_some() {
5746 bail!(Trap::WaitableSyncAndAsync);
5747 }
5748 Ok(())
5749 }
5750
5751 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5757 if let Some(set) = self.common(state)?.set
5758 && state.get_mut(set)?.is_sync_call_set
5759 {
5760 bail!(Trap::WaitableSyncAndAsync);
5761 }
5762 Ok(())
5763 }
5764
5765 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5769 log::trace!("set event for {self:?}: {event:?}");
5770 self.common(state)?.event = event;
5771 self.mark_ready(state)
5772 }
5773
5774 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5776 let common = self.common(state)?;
5777 let event = common.event.take();
5778 if let Some(set) = self.common(state)?.set {
5779 state.get_mut(set)?.ready.remove(self);
5780 }
5781
5782 Ok(event)
5783 }
5784
5785 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5789 if let Some(set) = self.common(state)?.set {
5790 state.get_mut(set)?.ready.insert(*self);
5791 state.wake_waiter(set)?;
5792 }
5793 Ok(())
5794 }
5795
5796 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5799 match self {
5800 Self::Host(task) => state.delete_host_task(*task),
5801 Self::Guest(task) => state.decrement_task_ref_count(*task),
5802 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5803 }
5804 }
5805}
5806
5807impl fmt::Debug for Waitable {
5808 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5809 match self {
5810 Self::Host(id) => write!(f, "{id:?}"),
5811 Self::Guest(id) => write!(f, "{id:?}"),
5812 Self::Transmit(id) => write!(f, "{id:?}"),
5813 }
5814 }
5815}
5816
5817#[derive(Default)]
5819struct WaitableSet {
5820 ready: BTreeSet<Waitable>,
5822 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5824 num_waiting: usize,
5827 is_sync_call_set: bool,
5830}
5831
5832impl WaitableSet {
5833 fn stop_waiting(&mut self) -> Result<()> {
5835 self.num_waiting = match self.num_waiting.checked_sub(1) {
5836 Some(n) => n,
5837 None => bail_bug!("waiter not accounted for in waitable set"),
5838 };
5839 Ok(())
5840 }
5841}
5842
5843impl TableDebug for WaitableSet {
5844 fn type_name() -> &'static str {
5845 "WaitableSet"
5846 }
5847}
5848
5849type RawLower =
5851 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5852
5853type RawLift = Box<
5855 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5856>;
5857
5858type LiftedResult = Box<dyn Any + Send + Sync>;
5862
5863struct DummyResult;
5866
5867#[derive(Default)]
5869pub struct ConcurrentInstanceState {
5870 backpressure: u16,
5872 do_not_enter: bool,
5874 do_not_suspend: bool,
5877 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5880}
5881
5882impl ConcurrentInstanceState {
5883 pub fn pending_is_empty(&self) -> bool {
5884 self.pending.is_empty()
5885 }
5886}
5887
5888#[derive(Debug, Copy, Clone)]
5889pub(crate) enum CurrentThread {
5890 Guest(QualifiedThreadId),
5893 Host(TableId<HostTask>),
5895 DeferredHost(QualifiedThreadId),
5898 None,
5901}
5902
5903impl CurrentThread {
5904 fn guest(&self) -> Option<&QualifiedThreadId> {
5905 match self {
5906 Self::Guest(id) => Some(id),
5907 _ => None,
5908 }
5909 }
5910
5911 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5912 match self {
5913 Self::Guest(id) => Some(id.task),
5914 _ => None,
5915 }
5916 }
5917
5918 fn is_none(&self) -> bool {
5919 matches!(self, Self::None)
5920 }
5921}
5922
5923impl From<QualifiedThreadId> for CurrentThread {
5924 fn from(id: QualifiedThreadId) -> Self {
5925 Self::Guest(id)
5926 }
5927}
5928
5929impl From<TableId<HostTask>> for CurrentThread {
5930 fn from(id: TableId<HostTask>) -> Self {
5931 Self::Host(id)
5932 }
5933}
5934
5935enum Priority {
5936 Switch,
5937 High,
5938 Low,
5939}
5940
5941pub struct ConcurrentState {
5943 unforced_current_thread: CurrentThread,
5949
5950 deferred_host_call_context: Option<CallContext>,
5956
5957 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5962 table: AlwaysMut<ResourceTable>,
5964 switch_item: Option<WorkItem>,
5972 next_switch_item: Option<WorkItem>,
5978 high_priority: VecDeque<WorkItem>,
5980 low_priority: VecDeque<WorkItem>,
5982 suspend_reason: Option<SuspendReason>,
5986 worker: Option<StoreFiber<'static>>,
5990 worker_item: Option<WorkerItem>,
5992
5993 global_error_context_ref_counts:
6006 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
6007
6008 interesting_tasks: usize,
6021
6022 interesting_tasks_empty_waker: Option<Waker>,
6026
6027 ready_for_concurrent_call_waker: Option<Waker>,
6032
6033 event_loop_running: bool,
6035
6036 saved_next_switch_items: Vec<Option<WorkItem>>,
6039
6040 #[cfg(feature = "task-group-hook")]
6042 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6043}
6044
6045impl Default for ConcurrentState {
6046 fn default() -> Self {
6047 Self {
6048 unforced_current_thread: CurrentThread::None,
6049 deferred_host_call_context: None,
6050 table: AlwaysMut::new(ResourceTable::new()),
6051 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6052 switch_item: None,
6053 next_switch_item: None,
6054 high_priority: VecDeque::new(),
6055 low_priority: VecDeque::new(),
6056 suspend_reason: None,
6057 worker: None,
6058 worker_item: None,
6059 global_error_context_ref_counts: BTreeMap::new(),
6060 interesting_tasks: 0,
6061 interesting_tasks_empty_waker: None,
6062 ready_for_concurrent_call_waker: None,
6063 event_loop_running: false,
6064 saved_next_switch_items: Vec::new(),
6065 #[cfg(feature = "task-group-hook")]
6066 task_group_hook: None,
6067 }
6068 }
6069}
6070
6071impl ConcurrentState {
6072 pub(crate) fn take_fibers_and_futures(
6089 &mut self,
6090 fibers: &mut Vec<StoreFiber<'static>>,
6091 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6092 ) {
6093 let ConcurrentState {
6094 table,
6095 worker,
6096 switch_item,
6097 next_switch_item,
6098 high_priority,
6099 low_priority,
6100 saved_next_switch_items,
6101 futures: pending_futures,
6102 worker_item: _,
6103 unforced_current_thread: _,
6104 deferred_host_call_context: _,
6105 suspend_reason: _,
6106 global_error_context_ref_counts: _,
6107 interesting_tasks: _,
6108 interesting_tasks_empty_waker: _,
6109 ready_for_concurrent_call_waker: _,
6110 event_loop_running: _,
6111 #[cfg(feature = "task-group-hook")]
6112 task_group_hook: _,
6113 } = self;
6114
6115 let mut items = Vec::new();
6116 for (_, entry) in table.get_mut().iter_mut() {
6117 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6118 for mode in mem::take(&mut set.waiting).into_values() {
6119 match mode {
6120 WaitMode::Fiber(fiber) => {
6121 fibers.push(fiber);
6122 }
6123 WaitMode::Callback(_) => {}
6124 }
6125 }
6126 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6127 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6128 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6129 fibers.push(fiber);
6130 }
6131 GuestThreadState::NotStartedImplicit
6132 | GuestThreadState::NotStartedExplicit(_)
6133 | GuestThreadState::Running
6134 | GuestThreadState::Completed => {}
6135 }
6136 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6137 if let Some(item) = item.take() {
6138 items.push(item);
6139 }
6140 }
6141 }
6142
6143 if let Some(fiber) = worker.take() {
6144 fibers.push(fiber);
6145 }
6146
6147 let mut handle_item = |item| match item {
6148 WorkItem::ResumeFiber { fiber, .. } => {
6149 fibers.push(fiber);
6150 }
6151 WorkItem::PushFuture(future) => {
6152 pending_futures
6153 .get_mut()
6154 .as_mut()
6155 .unwrap()
6156 .push(future.into_inner());
6157 }
6158 WorkItem::ResumeThread { .. }
6159 | WorkItem::GuestCall { .. }
6160 | WorkItem::WorkerFunction(_) => {}
6161 };
6162
6163 for item in items {
6164 handle_item(item);
6165 }
6166 if let Some(item) = switch_item.take() {
6167 handle_item(item);
6168 }
6169 if let Some(item) = next_switch_item.take() {
6170 handle_item(item);
6171 }
6172 for item in mem::take(high_priority) {
6173 handle_item(item);
6174 }
6175 for item in mem::take(low_priority) {
6176 handle_item(item);
6177 }
6178 for item in mem::take(saved_next_switch_items)
6179 .into_iter()
6180 .filter_map(|v| v)
6181 {
6182 handle_item(item);
6183 }
6184
6185 if let Some(them) = pending_futures.get_mut().take() {
6186 futures.push(them);
6187 }
6188 }
6189
6190 #[cfg(feature = "gc")]
6191 pub(crate) fn trace_fiber_roots(
6192 &mut self,
6193 modules: &ModuleRegistry,
6194 unwind: &dyn Unwind,
6195 gc_roots_list: &mut GcRootsList,
6196 ) {
6197 let ConcurrentState {
6198 table,
6199 worker,
6200 switch_item,
6201 next_switch_item,
6202 high_priority,
6203 low_priority,
6204 saved_next_switch_items,
6205
6206 futures: _,
6210
6211 worker_item: _,
6213 unforced_current_thread: _,
6214 deferred_host_call_context: _,
6215 suspend_reason: _,
6216 global_error_context_ref_counts: _,
6217 interesting_tasks: _,
6218 interesting_tasks_empty_waker: _,
6219 ready_for_concurrent_call_waker: _,
6220 event_loop_running: _,
6221 #[cfg(feature = "task-group-hook")]
6222 task_group_hook: _,
6223 } = self;
6224
6225 for (_, entry) in table.get_mut().iter_mut() {
6226 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6227 for mode in set.waiting.values_mut() {
6228 match mode {
6229 WaitMode::Fiber(fiber) => {
6230 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6231 }
6232 WaitMode::Callback(_) => {}
6233 }
6234 }
6235 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6236 match &mut thread.state {
6237 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6238 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6239 }
6240 GuestThreadState::NotStartedImplicit
6241 | GuestThreadState::NotStartedExplicit(_)
6242 | GuestThreadState::Running
6243 | GuestThreadState::Completed => {}
6244 }
6245 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6246 entry.downcast_mut::<Option<WorkItem>>()
6247 {
6248 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6249 }
6250 }
6251
6252 if let Some(fiber) = worker {
6253 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6254 }
6255
6256 let mut handle_item = |item: &mut WorkItem| match item {
6257 WorkItem::ResumeFiber { fiber, .. } => {
6258 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6259 }
6260 WorkItem::PushFuture(_future) => {
6261 }
6264 WorkItem::ResumeThread { .. }
6265 | WorkItem::GuestCall { .. }
6266 | WorkItem::WorkerFunction(_) => {}
6267 };
6268
6269 if let Some(item) = switch_item {
6270 handle_item(item);
6271 }
6272 if let Some(item) = next_switch_item {
6273 handle_item(item);
6274 }
6275 for item in high_priority {
6276 handle_item(item);
6277 }
6278 for item in low_priority {
6279 handle_item(item);
6280 }
6281 for item in saved_next_switch_items
6282 .iter_mut()
6283 .filter_map(|v| v.as_mut())
6284 {
6285 handle_item(item);
6286 }
6287 }
6288
6289 fn schedule_suspended_thread(
6290 &mut self,
6291 thread: QualifiedThreadId,
6292 instance: RuntimeInstance,
6293 priority: Priority,
6294 ) -> Result<()> {
6295 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6297 bail_bug!("switch item already set");
6298 }
6299 let slot = &mut self.get_mut(thread.thread)?.state;
6300 if !matches!(slot, GuestThreadState::Suspended(_)) {
6301 bail!(Trap::CannotResumeThread);
6302 }
6303 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6304 else {
6305 unreachable!()
6306 };
6307 let item = WorkItem::ResumeFiber {
6308 instance,
6309 thread,
6310 fiber,
6311 };
6312 match priority {
6313 Priority::Switch => self.switch_item = Some(item),
6314 Priority::High => self.push_high_priority(item),
6315 Priority::Low => self.push_low_priority(item),
6316 }
6317 Ok(())
6318 }
6319
6320 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6321 let slot = &mut self.get_mut(thread)?.state;
6322 slot.check_no_fiber()?;
6323 *slot = GuestThreadState::Running;
6324 Ok(())
6325 }
6326
6327 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6328 let saved = self.push(None::<WorkItem>)?;
6331 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6332 *slot = self.next_switch_item.take();
6333 Ok(saved)
6334 }
6335
6336 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6337 if self.next_switch_item.is_some() {
6338 bail_bug!("next switch item already set when restoring");
6339 }
6340 self.next_switch_item = self.delete(saved)?;
6341 Ok(())
6342 }
6343
6344 fn take_callback_waiter(
6345 &mut self,
6346 set: TableId<WaitableSet>,
6347 thread: QualifiedThreadId,
6348 ) -> Result<Instance> {
6349 let waiting = &mut self.get_mut(set)?.waiting;
6350 let instance = match waiting.get(&thread) {
6351 Some(WaitMode::Callback(instance)) => *instance,
6352 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6353 };
6354 waiting.remove(&thread);
6355 Ok(instance)
6356 }
6357
6358 fn push<V: Send + Sync + 'static>(
6359 &mut self,
6360 value: V,
6361 ) -> Result<TableId<V>, ResourceTableError> {
6362 self.table.get_mut().push(value).map(TableId::from)
6363 }
6364
6365 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6366 self.table.get_mut().get_mut(&Resource::from(id))
6367 }
6368
6369 pub fn add_child<T: 'static, U: 'static>(
6370 &mut self,
6371 child: TableId<T>,
6372 parent: TableId<U>,
6373 ) -> Result<(), ResourceTableError> {
6374 self.table
6375 .get_mut()
6376 .add_child(Resource::from(child), Resource::from(parent))
6377 }
6378
6379 pub fn remove_child<T: 'static, U: 'static>(
6380 &mut self,
6381 child: TableId<T>,
6382 parent: TableId<U>,
6383 ) -> Result<(), ResourceTableError> {
6384 self.table
6385 .get_mut()
6386 .remove_child(Resource::from(child), Resource::from(parent))
6387 }
6388
6389 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6390 self.table.get_mut().delete(Resource::from(id))
6391 }
6392
6393 fn push_future(&mut self, future: HostTaskFuture) {
6394 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6401 }
6402
6403 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6404 if self.switch_item.is_some() {
6405 self.push_high_priority(item);
6408 bail_bug!("switch item already set");
6409 }
6410
6411 self.switch_item = Some(item);
6412 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6413
6414 Ok(())
6415 }
6416
6417 fn take_next_switch_item(&mut self) -> Result<()> {
6418 if let Some(item) = self.next_switch_item.take() {
6419 self.set_switch_item(item)?;
6420 }
6421 Ok(())
6422 }
6423
6424 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6427 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6430 return Ok(());
6431 };
6432 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6433 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6434 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6435 }
6436 let instance = self.get_mut(thread.task)?.instance;
6437 self.get_mut(thread.thread)?.wake_on_cancel.take();
6438 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6439
6440 let item = match mode {
6441 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6442 instance,
6443 thread,
6444 fiber,
6445 },
6446 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6447 instance,
6448 call: GuestCall {
6449 thread,
6450 kind: GuestCallKind::DeliverEvent {
6451 instance: callback_instance,
6452 set: Some(set),
6453 },
6454 },
6455 },
6456 };
6457 self.push_high_priority(item);
6458 Ok(())
6459 }
6460
6461 fn push_high_priority(&mut self, item: WorkItem) {
6462 self.high_priority.push_front(item);
6463 log::trace!(
6464 "push high priority: {:?}",
6465 self.high_priority.front().unwrap()
6466 );
6467 }
6468
6469 fn push_low_priority(&mut self, item: WorkItem) {
6470 self.low_priority.push_front(item);
6471 log::trace!(
6472 "push low priority: {:?}",
6473 self.low_priority.front().unwrap()
6474 );
6475 }
6476
6477 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6478 match priority {
6479 Priority::Switch => self.set_switch_item(item)?,
6480 Priority::High => self.push_high_priority(item),
6481 Priority::Low => self.push_low_priority(item),
6482 }
6483
6484 Ok(())
6485 }
6486
6487 fn promote_instance_local_thread_work_item(
6488 &mut self,
6489 current_instance: RuntimeInstance,
6490 ) -> Result<bool> {
6491 log::trace!("promote thread work items for {current_instance:?}");
6492
6493 self.promote_work_item_matching(|item: &WorkItem| {
6494 let result = match item {
6495 WorkItem::ResumeThread { instance, .. }
6496 | WorkItem::ResumeFiber { instance, .. }
6497 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6498 _ => false,
6499 };
6500
6501 log::trace!("candidate {item:?}: {result}");
6502 result
6503 })
6504 }
6505
6506 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6507 self.promote_work_item_matching(|item: &WorkItem| match item {
6508 WorkItem::ResumeThread {
6509 thread: item_thread,
6510 ..
6511 }
6512 | WorkItem::GuestCall {
6513 call:
6514 GuestCall {
6515 thread: item_thread,
6516 ..
6517 },
6518 ..
6519 } => *item_thread == thread,
6520 _ => false,
6521 })
6522 }
6523
6524 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6525 where
6526 F: FnMut(&WorkItem) -> bool,
6527 {
6528 if self.switch_item.is_none() {
6535 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6536 self.switch_item = self.high_priority.remove(index);
6537 }
6538 }
6539 if self.switch_item.is_none() {
6540 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6541 self.switch_item = self.low_priority.remove(index);
6542 }
6543 }
6544 Ok(self.switch_item.is_some())
6545 }
6546
6547 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6550 match task {
6551 Scope::HostId(task) => {
6552 let task: TableId<HostTask> = TableId::new(task);
6553 Ok(&mut self.get_mut(task)?.call_context)
6554 }
6555 Scope::Id(task) => {
6556 let task: TableId<GuestTask> = TableId::new(task);
6557 Ok(&mut self.get_mut(task)?.call_context)
6558 }
6559 }
6560 }
6561
6562 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6563 self.deferred_host_call_context.as_mut()
6564 }
6565
6566 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6567 match self.futures.get_mut().as_mut() {
6568 Some(f) => Ok(f),
6569 None => bail_bug!("futures field of concurrent state is currently taken"),
6570 }
6571 }
6572
6573 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6574 self.table.get_mut()
6575 }
6576
6577 fn debug_assert_deferred_host_invariant(&self) {
6578 debug_assert_eq!(
6579 self.deferred_host_call_context.is_some(),
6580 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6581 "a deferred host thread and call context must exist together",
6582 );
6583 }
6584
6585 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6586 self.debug_assert_deferred_host_invariant();
6587 let caller = match self.unforced_current_thread {
6588 CurrentThread::DeferredHost(caller) => caller,
6589 thread => return Ok(thread),
6590 };
6591
6592 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6594 let task = self.push(task)?;
6595 let call_context = self
6596 .deferred_host_call_context
6597 .take()
6598 .expect("deferred host call context should be present");
6599 self.get_mut(task)
6600 .expect("newly inserted host task should be present")
6601 .call_context = call_context;
6602 self.unforced_current_thread = CurrentThread::Host(task);
6603 self.debug_assert_deferred_host_invariant();
6604 log::trace!("new host task materialized {task:?}");
6605 Ok(CurrentThread::Host(task))
6606 }
6607
6608 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6609 match self.materialize_host_task()? {
6610 CurrentThread::Host(id) => Ok(Some(id)),
6611 CurrentThread::None => Ok(None),
6612 CurrentThread::Guest(_) => {
6613 bail_bug!("tried to materialize a host task id from a guest thread")
6614 }
6615 CurrentThread::DeferredHost(_) => {
6616 bail_bug!(
6617 "current thread is a deferred host thread which should have been materialized"
6618 )
6619 }
6620 }
6621 }
6622
6623 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6624 match self.materialize_host_task()? {
6625 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6626 _ => bail_bug!("current scope is not a deferred host scope"),
6627 }
6628 }
6629
6630 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6631 let count = &mut self.get_mut(task)?.ref_count;
6632 *count += 1;
6633 log::trace!("increment {task:?} to {count}");
6634 Ok(())
6635 }
6636
6637 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6638 let count = &mut self.get_mut(task)?.ref_count;
6639 if *count == 0 {
6640 bail_bug!("can't decrement task ref count that's already zero");
6641 }
6642 *count -= 1;
6643 log::trace!("decrement {task:?} to {count}");
6644 if *count == 0 {
6645 let task = self.delete(task)?;
6646
6647 self.decrement_group_ref_count(task.group)?;
6648
6649 debug_assert!(task.decremented_interesting_task_count);
6656 }
6657 Ok(())
6658 }
6659
6660 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6661 log::trace!("delete host task {task:?}");
6662 let task = self.delete(task)?;
6663
6664 self.decrement_group_ref_count(task.group)
6665 }
6666}
6667
6668fn for_any_lower<
6671 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6672>(
6673 fun: F,
6674) -> F {
6675 fun
6676}
6677
6678fn for_any_lift<
6680 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6681>(
6682 fun: F,
6683) -> F {
6684 fun
6685}
6686
6687fn check_ambient_store(id: StoreId) {
6688 let message = "\
6689 `Future`s which depend on asynchronous component tasks, streams, or \
6690 futures to complete may only be polled from the event loop of the \
6691 store to which they belong. Please use \
6692 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6693 ";
6694 tls::try_get(|store| {
6695 let matched = match store {
6696 tls::TryGet::Some(store) => store.id() == id,
6697 tls::TryGet::Taken | tls::TryGet::None => false,
6698 };
6699
6700 if !matched {
6701 panic!("{message}")
6702 }
6703 });
6704}
6705
6706fn unpack_callback_code(code: u32) -> (u32, u32) {
6707 (code & 0xF, code >> 4)
6708}
6709
6710struct WaitableCheckParams {
6714 set: TableId<WaitableSet>,
6715 options: OptionsIndex,
6716 payload: u32,
6717}
6718
6719enum WaitableCheck {
6722 Wait,
6723 Poll,
6724}
6725
6726pub(crate) struct PreparedCall<R> {
6728 handle: Func,
6730 thread: QualifiedThreadId,
6732 param_count: usize,
6734 rx: oneshot::Receiver<LiftedResult>,
6737 _phantom: PhantomData<R>,
6738}
6739
6740impl<R> PreparedCall<R> {
6741 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6748 store
6749 .concurrent_state_mut()?
6750 .increment_task_ref_count(self.thread.task)?;
6751
6752 Ok(TaskId {
6753 task: self.thread.task,
6754 })
6755 }
6756}
6757
6758pub(crate) struct TaskId {
6760 task: TableId<GuestTask>,
6761}
6762
6763impl TaskId {
6764 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6776 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6777 if !task.already_lowered_parameters() {
6778 task.lower_params = Some(Box::new(|store, _| {
6783 store.set_trapped();
6784 bail!("the host future for this call was dropped before it started")
6785 }));
6786 } else {
6787 _ = store
6792 .concurrent_state_mut()?
6793 .decrement_task_ref_count(self.task);
6794 }
6795
6796 Ok(())
6797 }
6798}
6799
6800pub(crate) fn prepare_call<T, R>(
6806 mut store: StoreContextMut<T>,
6807 handle: Func,
6808 param_count: usize,
6809 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6810 + Send
6811 + Sync
6812 + 'static,
6813 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6814 + Send
6815 + Sync
6816 + 'static,
6817) -> Result<PreparedCall<R>> {
6818 if !store.0.may_enter() {
6822 bail!(Trap::CannotEnterComponent);
6823 }
6824
6825 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6826
6827 let instance = handle.instance().id().get(store.0);
6828 let options = &instance.component().env_component().options[options];
6829 let ty = &instance.component().types()[ty];
6830 let async_typed = ty.async_;
6831 let async_lifted = raw_options.async_;
6832 let task_return_type = ty.results;
6833 let component_instance = raw_options.instance;
6834 let callback = options.callback.map(|i| instance.runtime_callback(i));
6835 let memory = options
6836 .memory()
6837 .map(|i| instance.runtime_memory(i))
6838 .map(SendSyncPtr::new);
6839 let string_encoding = options.string_encoding;
6840 let token = StoreToken::new(store.as_context_mut());
6841 let caller = store.0.materialize_host_task_id()?;
6842 let state = store.0.concurrent_state_mut()?;
6843
6844 let (tx, rx) = oneshot::channel();
6845
6846 let instance = handle.instance().runtime_instance(component_instance);
6847 let thread = GuestTask::new(
6848 state,
6849 Box::new(for_any_lower(move |store, params| {
6850 lower_params(token.as_context_mut(store), params)
6851 })),
6852 LiftResult {
6853 lift: Box::new(for_any_lift(move |store, result| {
6854 lift_result(store, result)
6855 })),
6856 ty: task_return_type,
6857 memory,
6858 string_encoding,
6859 },
6860 Caller::Host {
6861 tx: Some(tx),
6862 caller,
6863 },
6864 callback.map(|callback| {
6865 let callback = SendSyncPtr::new(callback);
6866 let instance = handle.instance();
6867 Box::new(move |store: &mut dyn VMStore, event, handle| {
6868 let store = token.as_context_mut(store);
6869 unsafe { instance.call_callback(store, callback, event, handle) }
6872 }) as CallbackFn
6873 }),
6874 instance,
6875 async_typed,
6876 async_lifted,
6877 )?;
6878
6879 Ok(PreparedCall {
6880 handle,
6881 thread,
6882 param_count,
6883 rx,
6884 _phantom: PhantomData,
6885 })
6886}
6887
6888pub(crate) struct StagedCall<R> {
6889 store: StoreId,
6890 rx: oneshot::Receiver<LiftedResult>,
6891 _marker: PhantomData<fn() -> R>,
6892 group: TaskGroupId,
6900}
6901
6902impl<R> StagedCall<R> {
6903 pub(crate) fn new<T: 'static>(
6914 mut store: StoreContextMut<T>,
6915 prepared: PreparedCall<R>,
6916 ) -> Result<StagedCall<R>> {
6917 let PreparedCall {
6918 handle,
6919 thread,
6920 param_count,
6921 rx,
6922 ..
6923 } = prepared;
6924
6925 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6926
6927 let state = store.0.concurrent_state_mut()?;
6928 let group = state.get_mut(thread.task)?.group;
6929 state.decrement_task_ref_count(thread.task)?;
6930
6931 Ok(StagedCall {
6932 store: store.0.id(),
6933 rx,
6934 _marker: PhantomData,
6935 group,
6936 })
6937 }
6938}
6939
6940impl<R> Future for StagedCall<R>
6941where
6942 R: 'static,
6943{
6944 type Output = Result<R>;
6945
6946 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6947 check_ambient_store(self.store);
6948 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6949 Ok(r) => match r.downcast() {
6950 Ok(r) => Ok(*r),
6951 Err(_) => bail_bug!("wrong type of value produced"),
6952 },
6953 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6954 })
6955 }
6956}
6957
6958fn stage_call0<T: 'static>(
6961 store: StoreContextMut<T>,
6962 handle: Func,
6963 guest_thread: QualifiedThreadId,
6964 param_count: usize,
6965) -> Result<()> {
6966 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6967 let is_concurrent = raw_options.async_;
6968 let callback = raw_options.callback;
6969 let instance = handle.instance();
6970 let callee = handle.lifted_core_func(store.0);
6971 let post_return = raw_options
6972 .post_return
6973 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6974 let callback = callback.map(|i| {
6975 let instance = instance.id().get(store.0);
6976 SendSyncPtr::new(instance.runtime_callback(i))
6977 });
6978
6979 log::trace!("queueing call {guest_thread:?}");
6980
6981 unsafe {
6985 instance.stage_call(
6986 store,
6987 guest_thread,
6988 SendSyncPtr::new(callee),
6989 param_count,
6990 1,
6991 is_concurrent,
6992 callback,
6993 post_return.map(SendSyncPtr::new),
6994 true,
6995 )
6996 }
6997}
6998
6999#[cfg(all(test, feature = "cranelift", feature = "wat"))]
7000mod tests {
7001 use super::*;
7002 use crate::component::{Component, Linker};
7003 use crate::store::AsStoreOpaque;
7004 use crate::{Config, Engine};
7005
7006 fn host_subtask(
7007 state: HostTaskState,
7008 event: Option<Event>,
7009 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
7010 let mut config = Config::new();
7011 config.wasm_component_model_async(true);
7012 let engine = Engine::new(&config)?;
7013 let component = Component::new(&engine, "(component)")?;
7014 let mut store = Store::new(&engine, ());
7015 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
7016 let store_opaque = store.as_store_opaque();
7017 let concurrent_state = store_opaque.concurrent_state_mut()?;
7018 let group = concurrent_state.make_task_group()?;
7021 let task = concurrent_state.push(HostTask {
7022 common: WaitableCommon::default(),
7023 call_context: CallContext::default(),
7024 state,
7025 group,
7026 })?;
7027 let (state, counter) = store_opaque.instance_state_and_handle_counter(
7028 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
7029 );
7030 let handle = state
7031 .handle_table()
7032 .subtask_insert_host(counter, task.rep())?;
7033 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7034 common.handle = Some(handle);
7035 common.event = event;
7036 Ok((store, instance, task, handle))
7037 }
7038
7039 #[test]
7040 fn host_subtask_drop_during_cancellation() -> Result<()> {
7041 for abort_completed in [false, true] {
7042 let (handle, future) = JoinHandle::run(future::pending::<()>());
7043 let mut future = pin!(future);
7044 let (mut store, instance, task, handle) =
7045 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7046 let store = store.as_store_opaque();
7047 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7048 assert_eq!(
7049 instance.subtask_cancel(store, caller, true, handle)?,
7050 BLOCKED
7051 );
7052 if abort_completed {
7053 assert!(matches!(
7056 future
7057 .as_mut()
7058 .poll(&mut Context::from_waker(Waker::noop())),
7059 Poll::Ready(None),
7060 ));
7061 }
7062 for async_ in [false, true] {
7063 let err = instance
7064 .subtask_cancel(store, caller, async_, handle)
7065 .unwrap_err();
7066 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7067 }
7068 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7069 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7070 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7071 }
7072 Ok(())
7073 }
7074
7075 #[test]
7076 fn host_subtask_cancel_after_completion() -> Result<()> {
7077 for async_ in [false, true] {
7078 let (mut store, instance, task, handle) = host_subtask(
7079 HostTaskState::CalleeDone { cancelled: false },
7080 Some(Event::Subtask {
7081 status: Status::Returned,
7082 }),
7083 )?;
7084 let store = store.as_store_opaque();
7085 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7086 assert_eq!(
7087 instance.subtask_cancel(store, caller, async_, handle)?,
7088 Status::Returned as u32,
7089 );
7090 let err = instance
7091 .subtask_cancel(store, caller, async_, handle)
7092 .unwrap_err();
7093 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7094 instance.subtask_drop(store, caller, handle)?;
7095 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7096 }
7097 Ok(())
7098 }
7099
7100 #[test]
7101 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7102 for (cancelled, status) in [
7103 (false, Status::Returned),
7104 (true, Status::Returned),
7105 (true, Status::ReturnCancelled),
7106 ] {
7107 for delivered in [false, true] {
7108 let event = if delivered {
7109 None
7110 } else {
7111 Some(Event::Subtask { status })
7112 };
7113 let (mut store, instance, task, handle) =
7114 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7115 let store = store.as_store_opaque();
7116 let result = instance.subtask_drop(
7117 store,
7118 RuntimeComponentInstanceIndex::from_u32(0),
7119 handle,
7120 );
7121 if delivered {
7122 result?;
7123 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7124 } else {
7125 let err = result.unwrap_err();
7126 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7127 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7128 }
7129 }
7130 }
7131 Ok(())
7132 }
7133}