1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::store::ComponentInstanceId;
56use crate::component::{
57 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
58};
59use crate::fiber::{self, StoreFiber, StoreFiberYield};
60use crate::hash_set::HashSet;
61#[cfg(feature = "gc")]
62use crate::module::ModuleRegistry;
63use crate::prelude::*;
64use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
65#[cfg(feature = "gc")]
66use crate::vm::GcRootsList;
67use crate::vm::VMScope;
68use crate::vm::component::ScopeKind;
69use crate::vm::component::{
70 CallContext, ComponentInstance, CurrentScope, HandleCounter, HandleTableInsertion,
71 InstanceState,
72};
73use crate::vm::{
74 AlwaysMut, SendSyncPtr, UncaughtException, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore,
75};
76use crate::{AsContext, AsContextMut, Result, StoreContext, StoreContextMut, ValRaw, bail};
77use crate::{Instance as ModuleInstance, bail_bug};
78use alloc::borrow::ToOwned;
79use alloc::collections::btree_map::Entry;
80use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
81use core::any::Any;
82use core::cell::UnsafeCell;
83use core::fmt;
84use core::future;
85use core::future::Future;
86use core::marker::PhantomData;
87use core::mem::{self, ManuallyDrop, MaybeUninit};
88use core::ops::DerefMut;
89use core::pin::{Pin, pin};
90use core::ptr::{self, NonNull};
91use core::task::{Context, Poll, Waker};
92use futures::channel::oneshot;
93use futures::stream::{FuturesUnordered, StreamExt};
94use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
95use smallvec::SmallVec;
96use table::{TableDebug, TableId};
97use wasmtime_environ::component::{
98 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
99 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
100 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
101 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
102 TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
103};
104use wasmtime_environ::packed_option::ReservedValue;
105use wasmtime_environ::{ModuleInternedTypeIndex, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
106#[cfg(feature = "gc")]
107use wasmtime_unwinder::Unwind;
108
109pub use abort::JoinHandle;
110pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
111pub use future_stream_any::{FutureAny, StreamAny};
112pub use futures_and_streams::{
113 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
114 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
115 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
116};
117pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
118#[cfg(feature = "task-group-hook")]
119pub use task_group_hook::TaskGroupHook;
120pub use task_group_hook::TaskGroupId;
121
122mod abort;
123mod error_contexts;
124mod func;
125mod future_stream_any;
126mod futures_and_streams;
127pub(crate) mod table;
128#[cfg(feature = "task-group-hook")]
129mod task_group_hook;
130#[cfg(not(feature = "task-group-hook"))]
131mod task_group_hook_disabled;
132#[cfg(not(feature = "task-group-hook"))]
133use task_group_hook_disabled as task_group_hook;
134pub(crate) mod tls;
135
136const BLOCKED: u32 = 0xffff_ffff;
139
140#[derive(Clone, Copy, Eq, PartialEq, Debug)]
142pub enum Status {
143 Starting = 0,
144 Started = 1,
145 Returned = 2,
146 StartCancelled = 3,
147 ReturnCancelled = 4,
148}
149
150impl Status {
151 pub fn pack(self, waitable: Option<u32>) -> u32 {
157 assert!(matches!(self, Status::Returned) == waitable.is_none());
158 let waitable = waitable.unwrap_or(0);
159 assert!(waitable < (1 << 28));
160 (waitable << 4) | (self as u32)
161 }
162}
163
164#[derive(Clone, Copy, Debug)]
167enum Event {
168 None,
169 Subtask {
170 status: Status,
171 },
172 StreamRead {
173 code: ReturnCode,
174 pending: Option<(TypeStreamTableIndex, u32)>,
175 },
176 StreamWrite {
177 code: ReturnCode,
178 pending: Option<(TypeStreamTableIndex, u32)>,
179 },
180 FutureRead {
181 code: ReturnCode,
182 pending: Option<(TypeFutureTableIndex, u32)>,
183 },
184 FutureWrite {
185 code: ReturnCode,
186 pending: Option<(TypeFutureTableIndex, u32)>,
187 },
188 Cancelled,
189}
190
191impl Event {
192 fn parts(self) -> (u32, u32) {
197 const EVENT_NONE: u32 = 0;
198 const EVENT_SUBTASK: u32 = 1;
199 const EVENT_STREAM_READ: u32 = 2;
200 const EVENT_STREAM_WRITE: u32 = 3;
201 const EVENT_FUTURE_READ: u32 = 4;
202 const EVENT_FUTURE_WRITE: u32 = 5;
203 const EVENT_CANCELLED: u32 = 6;
204 match self {
205 Event::None => (EVENT_NONE, 0),
206 Event::Cancelled => (EVENT_CANCELLED, 0),
207 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
208 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
209 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
210 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
211 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
212 }
213 }
214}
215
216mod callback_code {
218 pub const EXIT: u32 = 0;
219 pub const YIELD: u32 = 1;
220 pub const WAIT: u32 = 2;
221}
222
223const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
225const START_FLAG_ASYNC_CALLER: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLER as u32;
226
227pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
233 store: StoreContextMut<'a, T>,
234 get_data: fn(&mut T) -> D::Data<'_>,
235}
236
237impl<'a, T, D> Access<'a, T, D>
238where
239 D: HasData + ?Sized,
240 T: 'static,
241{
242 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
244 Self { store, get_data }
245 }
246
247 pub fn data_mut(&mut self) -> &mut T {
249 self.store.data_mut()
250 }
251
252 pub fn get(&mut self) -> D::Data<'_> {
254 (self.get_data)(self.data_mut())
255 }
256
257 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
261 where
262 T: 'static,
263 {
264 let accessor = Accessor {
265 get_data: self.get_data,
266 token: StoreToken::new(self.store.as_context_mut()),
267 };
268 self.store
269 .as_context_mut()
270 .spawn_with_accessor(accessor, task)
271 }
272
273 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
276 self.get_data
277 }
278}
279
280impl<'a, T, D> AsContext for Access<'a, T, D>
281where
282 D: HasData + ?Sized,
283 T: 'static,
284{
285 type Data = T;
286
287 fn as_context(&self) -> StoreContext<'_, T> {
288 self.store.as_context()
289 }
290}
291
292impl<'a, T, D> AsContextMut for Access<'a, T, D>
293where
294 D: HasData + ?Sized,
295 T: 'static,
296{
297 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
298 self.store.as_context_mut()
299 }
300}
301
302pub struct Accessor<T: 'static, D = HasSelf<T>>
362where
363 D: HasData + ?Sized,
364{
365 token: StoreToken<T>,
366 get_data: fn(&mut T) -> D::Data<'_>,
367}
368
369pub trait AsAccessor {
386 type Data: 'static;
388
389 type AccessorData: HasData + ?Sized;
392
393 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
395}
396
397impl<T: AsAccessor + ?Sized> AsAccessor for &T {
398 type Data = T::Data;
399 type AccessorData = T::AccessorData;
400
401 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
402 T::as_accessor(self)
403 }
404}
405
406impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
407 type Data = T;
408 type AccessorData = D;
409
410 fn as_accessor(&self) -> &Accessor<T, D> {
411 self
412 }
413}
414
415const _: () = {
438 const fn assert<T: Send + Sync>() {}
439 assert::<Accessor<UnsafeCell<u32>>>();
440};
441
442impl<T> Accessor<T> {
443 pub(crate) fn new(token: StoreToken<T>) -> Self {
452 Self {
453 token,
454 get_data: |x| x,
455 }
456 }
457}
458
459impl<T, D> Accessor<T, D>
460where
461 D: HasData + ?Sized,
462{
463 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
481 tls::get(|vmstore| {
482 fun(Access {
483 store: self.token.as_context_mut(vmstore),
484 get_data: self.get_data,
485 })
486 })
487 }
488
489 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
492 self.get_data
493 }
494
495 pub fn with_getter<D2: HasData>(
512 &self,
513 get_data: fn(&mut T) -> D2::Data<'_>,
514 ) -> Accessor<T, D2> {
515 Accessor {
516 token: self.token,
517 get_data,
518 }
519 }
520
521 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
537 where
538 T: 'static,
539 {
540 let accessor = self.clone_for_spawn();
541 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
542 }
543
544 fn clone_for_spawn(&self) -> Self {
545 Self {
546 token: self.token,
547 get_data: self.get_data,
548 }
549 }
550
551 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
587 self.with(|mut access| {
588 let store = access.as_context_mut().0;
589 let state = store.concurrent_state_mut_without_forcing_current_thread();
590 if state.interesting_tasks == 0 {
591 Poll::Ready(())
592 } else {
593 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
594 Poll::Pending
595 }
596 })
597 }
598
599 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
616 self.with(|mut access| {
617 let store = access.as_context_mut().0;
618 let (_, _, _, raw_options) = func.abi_info(store);
619 let instance = func.instance().runtime_instance(raw_options.instance);
620 let state = store.instance_state(instance).concurrent_state();
621 if state.backpressure == 0 {
622 Poll::Ready(())
623 } else {
624 store
625 .concurrent_state_mut_without_forcing_current_thread()
626 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
627 Poll::Pending
628 }
629 })
630 }
631}
632
633pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
655 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
656where
657 D: HasData + ?Sized,
658{
659 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
661}
662
663impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
664where
665 T: 'static,
666 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
667 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
668 Fut: Future<Output = Result<()>> + Send + 'fut,
669 D: HasData,
670{
671 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
672 (self)(accessor)
673 }
674}
675
676enum CallerInfo {
679 Async {
681 params: Vec<ValRaw>,
682 has_result: bool,
683 },
684 Sync {
686 params: Vec<ValRaw>,
687 result_count: u32,
688 },
689}
690
691enum WaitMode {
693 Fiber(StoreFiber<'static>),
695 Callback(Instance),
698}
699
700impl fmt::Debug for WaitMode {
701 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702 match self {
703 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
704 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
705 }
706 }
707}
708
709#[derive(Debug)]
711enum SuspendReason {
712 Waiting {
715 set: TableId<WaitableSet>,
716 thread: QualifiedThreadId,
717 },
718 YieldingToSubtask { thread: QualifiedThreadId },
721 NeedWork,
724 Yielding { thread: QualifiedThreadId },
727 ExplicitlySuspending { thread: QualifiedThreadId },
730}
731
732enum GuestCallKind {
734 DeliverEvent {
737 instance: Instance,
739 set: Option<TableId<WaitableSet>>,
744 },
745 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
748 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
749}
750
751impl fmt::Debug for GuestCallKind {
752 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
753 match self {
754 Self::DeliverEvent { instance, set } => f
755 .debug_struct("DeliverEvent")
756 .field("instance", instance)
757 .field("set", set)
758 .finish(),
759 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
760 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
761 }
762 }
763}
764
765#[derive(Copy, Clone, Debug)]
767pub enum SuspensionTarget {
768 Resume(u32),
769 Promote(u32),
770 None,
771}
772
773#[derive(Copy, Clone, Debug)]
775pub enum ResumeThread {
776 Promote,
777 Resume,
778 ResumeLater,
779}
780
781#[derive(Debug)]
783struct GuestCall {
784 thread: QualifiedThreadId,
785 kind: GuestCallKind,
786}
787
788impl GuestCall {
789 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
800 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
801 let async_typed = task.async_typed;
802 let async_lifted = task.async_lifted;
803 let has_callback = task.callback.is_some();
804 let instance = task.instance;
805 let state = store.instance_state(instance).concurrent_state();
806
807 let ready = match &self.kind {
808 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
809 GuestCallKind::StartImplicit(_) => {
810 if async_typed {
811 let needs_exclusive = !async_lifted || has_callback;
814 !((needs_exclusive && state.do_not_enter) || state.backpressure > 0)
815 } else {
816 true
817 }
818 }
819 GuestCallKind::StartExplicit(_) => true,
820 };
821 log::trace!(
822 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
823 state.do_not_enter,
824 state.backpressure
825 );
826 Ok(ready)
827 }
828}
829
830enum WorkerItem {
832 GuestCall(GuestCall),
833 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
834}
835
836enum WorkItem {
839 PushFuture(AlwaysMut<HostTaskFuture>),
841 ResumeFiber {
843 instance: RuntimeInstance,
844 thread: QualifiedThreadId,
845 fiber: StoreFiber<'static>,
846 },
847 ResumeThread {
849 instance: RuntimeInstance,
850 thread: QualifiedThreadId,
851 },
852 GuestCall {
854 instance: RuntimeInstance,
855 call: GuestCall,
856 },
857 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
859}
860
861struct ResumingFiber<'a> {
867 store: &'a mut StoreOpaque,
868 fiber: Option<StoreFiber<'static>>,
869}
870
871impl Drop for ResumingFiber<'_> {
872 fn drop(&mut self) {
873 if let Some(mut fiber) = self.fiber.take() {
874 fiber.dispose(self.store);
875 }
876 }
877}
878
879impl fmt::Debug for WorkItem {
880 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
881 match self {
882 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
883 Self::ResumeFiber {
884 instance, thread, ..
885 } => f
886 .debug_struct("ResumeFiber")
887 .field("instance", instance)
888 .field("thread", thread)
889 .finish(),
890 Self::ResumeThread { instance, thread } => f
891 .debug_struct("ResumeThread")
892 .field("instance", instance)
893 .field("thread", thread)
894 .finish(),
895 Self::GuestCall { instance, call } => f
896 .debug_struct("GuestCall")
897 .field("instance", instance)
898 .field("call", call)
899 .finish(),
900 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
901 }
902 }
903}
904
905#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
907pub(crate) enum WaitResult {
908 Cancelled,
909 Completed,
910}
911
912pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
920 store: &mut dyn VMStore,
921 host_task: EnteredHostTask,
922 future: impl Future<Output = Result<R>> + Send + 'static,
923) -> Result<R> {
924 let mut future = Box::pin(future);
931 let poll = tls::set(store, || {
932 future
933 .as_mut()
934 .poll(&mut Context::from_waker(&Waker::noop()))
935 });
936
937 let caller = match host_task {
938 Some(caller) => caller,
939 None => bail_bug!("host task wasn't created but should have been"),
940 };
941
942 let task = match poll {
943 Poll::Ready(result) => return result,
945
946 Poll::Pending => {
951 let Some(task) = store.materialize_host_task_id()? else {
952 bail_bug!("current thread is not a host thread")
953 };
954
955 let future = Box::pin(async move {
958 let result = run_with_host_task_set(task, future).await??;
959 tls::get(move |store| {
960 let state = store.concurrent_state_mut()?;
961 let host_state = &mut state.get_mut(task)?.state;
962 assert!(matches!(host_state, HostTaskState::CalleeStarted));
963 *host_state = HostTaskState::CalleeFinished(Box::new(result));
964
965 Waitable::Host(task).set_event(
966 state,
967 Some(Event::Subtask {
968 status: Status::Returned,
969 }),
970 )?;
971
972 Ok(())
973 })
974 }) as HostTaskFuture;
975
976 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
977 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
978
979 let state = store.concurrent_state_mut()?;
980 state.push_future(future);
981
982 let set = state.get_mut(caller.thread)?.sync_call_set;
983 Waitable::Host(task).join(state, Some(set))?;
984
985 store.suspend(SuspendReason::Waiting {
986 set,
987 thread: caller,
988 })?;
989
990 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
994 task
995 }
996 };
997
998 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
1000 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
1001 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
1002 Ok(result) => *result,
1003 Err(_) => bail_bug!("host task finished with wrong type of result"),
1004 }),
1005 _ => bail_bug!("unexpected host task state after completion"),
1006 }
1007}
1008
1009fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
1011 match call.kind {
1012 GuestCallKind::DeliverEvent { instance, set } => {
1013 if let Some(set) = set {
1016 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1017 }
1018 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1019 Some(pair) => pair,
1020 None => match set {
1021 Some(set) => {
1026 log::trace!(
1027 "event for {:?} on {set:?} no longer present; waiting again",
1028 call.thread
1029 );
1030 return instance.wait_with_callback(
1031 store.store_opaque_mut(),
1032 call.thread,
1033 set,
1034 );
1035 }
1036 None => (Event::None, None),
1041 },
1042 };
1043 let state = store.concurrent_state_mut()?;
1044 if let Some(set) = set
1049 && waitable.is_none()
1050 && !state.get_mut(set)?.ready.is_empty()
1051 {
1052 state.wake_waiter(set)?;
1053 }
1054 let task = state.get_mut(call.thread.task)?;
1055 let runtime_instance = task.instance;
1056 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1057
1058 log::trace!(
1059 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1060 call.thread,
1061 );
1062
1063 instance.handle_callback_code(store, call.thread, runtime_instance, |store| {
1064 invoke_callback(store, call.thread.task, event, handle)
1065 })?;
1066 }
1067 GuestCallKind::StartImplicit(fun) => {
1068 fun(store)?;
1069 }
1070 GuestCallKind::StartExplicit(fun) => {
1071 fun(store)?;
1072 }
1073 }
1074
1075 Ok(())
1076}
1077
1078fn invoke_callback(
1081 store: &mut dyn VMStore,
1082 task: TableId<GuestTask>,
1083 event: Event,
1084 handle: u32,
1085) -> Result<u32> {
1086 let Some(callback) = store.concurrent_state_mut()?.get_mut(task)?.callback.take() else {
1087 bail_bug!("guest task callback field not present")
1088 };
1089
1090 let code = callback(store, event, handle)?;
1091
1092 store.concurrent_state_mut()?.get_mut(task)?.callback = Some(callback);
1093
1094 Ok(code)
1095}
1096
1097impl<T> Store<T> {
1098 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1100 where
1101 T: Send + 'static,
1102 {
1103 ensure!(
1104 self.as_context().0.concurrency_support(),
1105 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1106 );
1107 self.as_context_mut().run_concurrent(fun).await
1108 }
1109
1110 #[doc(hidden)]
1111 pub fn assert_concurrent_state_empty(&mut self) {
1112 self.as_context_mut().assert_concurrent_state_empty();
1113 }
1114
1115 #[doc(hidden)]
1116 pub fn concurrent_state_table_size(&mut self) -> usize {
1117 self.as_context_mut().concurrent_state_table_size()
1118 }
1119
1120 pub fn spawn(
1122 &mut self,
1123 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1124 ) -> Result<JoinHandle>
1125 where
1126 T: 'static,
1127 {
1128 self.as_context_mut().spawn(task)
1129 }
1130}
1131
1132impl<T> StoreContextMut<'_, T> {
1133 #[doc(hidden)]
1144 pub fn assert_concurrent_state_empty(self) {
1145 let store = self.0;
1146 store
1147 .store_data_mut()
1148 .components
1149 .assert_instance_states_empty();
1150 let state = store.concurrent_state_mut().unwrap();
1151 assert!(
1152 state.table.get_mut().is_empty(),
1153 "non-empty table: {:?}",
1154 state.table.get_mut()
1155 );
1156 assert!(state.switch_item.is_none());
1157 assert!(state.next_switch_item.is_none());
1158 assert!(state.high_priority.is_empty());
1159 assert!(state.low_priority.is_empty());
1160 assert!(state.saved_next_switch_items.is_empty());
1161 assert!(state.unforced_current_thread.is_none());
1162 assert!(state.deferred_host_call_context.is_none());
1163 assert!(state.futures_mut().unwrap().is_empty());
1164 assert!(state.global_error_context_ref_counts.is_empty());
1165 }
1166
1167 #[doc(hidden)]
1172 pub fn concurrent_state_table_size(&mut self) -> usize {
1173 self.0.concurrent_state_mut().unwrap().table.get_mut().len()
1174 }
1175
1176 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1186 where
1187 T: 'static,
1188 {
1189 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1190 self.spawn_with_accessor(accessor, task)
1191 }
1192
1193 fn spawn_with_accessor<D>(
1196 self,
1197 accessor: Accessor<T, D>,
1198 task: impl for<'fut> AccessorTask<'fut, T, D>,
1199 ) -> Result<JoinHandle>
1200 where
1201 T: 'static,
1202 D: HasData + ?Sized,
1203 {
1204 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1208 self.0
1209 .concurrent_state_mut()?
1210 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1211 Ok(handle)
1212 }
1213
1214 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1298 where
1299 T: Send + 'static,
1300 {
1301 ensure!(
1302 self.0.concurrency_support(),
1303 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1304 );
1305 self.do_run_concurrent(fun, false).await
1306 }
1307
1308 pub(super) async fn run_concurrent_trap_on_idle<R>(
1309 self,
1310 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1311 ) -> Result<R> {
1312 self.do_run_concurrent(fun, true).await
1313 }
1314
1315 async fn do_run_concurrent<R>(
1316 mut self,
1317 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1318 trap_on_idle: bool,
1319 ) -> Result<R> {
1320 debug_assert!(self.0.concurrency_support());
1321 self.0.check_event_loop_not_running()?;
1322 let token = StoreToken::new(self.as_context_mut());
1323
1324 struct Dropper<'a, T: 'static, V> {
1325 store: StoreContextMut<'a, T>,
1326 value: ManuallyDrop<V>,
1327 }
1328
1329 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1330 fn drop(&mut self) {
1331 self.store
1332 .0
1333 .concurrent_state_mut_already_forced_current_thread()
1334 .event_loop_running = false;
1335
1336 tls::set(self.store.0, || {
1337 unsafe { ManuallyDrop::drop(&mut self.value) }
1342 });
1343 }
1344 }
1345
1346 let accessor = &Accessor::new(token);
1347 self.0
1348 .concurrent_state_mut_already_forced_current_thread()
1349 .event_loop_running = true;
1350 let dropper = &mut Dropper {
1351 store: self,
1352 value: ManuallyDrop::new(fun(accessor)),
1353 };
1354 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1356
1357 let result = dropper
1358 .store
1359 .as_context_mut()
1360 .poll_until(future, trap_on_idle)
1361 .await;
1362
1363 if result.is_err() {
1364 dropper.store.0.set_trapped();
1365 }
1366
1367 result
1368 }
1369
1370 async fn poll_until<R>(
1376 mut self,
1377 mut future: Pin<&mut impl Future<Output = R>>,
1378 trap_on_idle: bool,
1379 ) -> Result<R> {
1380 struct Reset<'a, T: 'static> {
1381 store: StoreContextMut<'a, T>,
1382 futures: Option<FuturesUnordered<HostTaskFuture>>,
1383 }
1384
1385 impl<'a, T> Drop for Reset<'a, T> {
1386 fn drop(&mut self) {
1387 if let Some(futures) = self.futures.take() {
1388 *self
1389 .store
1390 .0
1391 .concurrent_state_mut_already_forced_current_thread()
1392 .futures
1393 .get_mut() = Some(futures);
1394 }
1395 }
1396 }
1397
1398 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1402 let mut turns_without_yield = 0;
1403
1404 loop {
1405 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1409 let mut reset = Reset {
1410 store: self.as_context_mut(),
1411 futures,
1412 };
1413 let mut next = match reset.futures.as_mut() {
1414 Some(f) => pin!(f.next()),
1415 None => bail_bug!("concurrent state missing futures field"),
1416 };
1417
1418 enum PollResult<R> {
1419 Complete(R),
1420 ProcessWork {
1421 ready: Option<WorkItem>,
1422 low_priority: bool,
1423 },
1424 }
1425
1426 let result = future::poll_fn(|cx| {
1427 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1430 return Poll::Ready(Ok(PollResult::Complete(value)));
1431 }
1432
1433 if reset.store.0.component_trapped() {
1442 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1443 }
1444
1445 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1449 Poll::Ready(Some(output)) => {
1450 match output {
1451 Err(e) => return Poll::Ready(Err(e)),
1452 Ok(()) => {}
1453 }
1454 Poll::Ready(true)
1455 }
1456 Poll::Ready(None) => Poll::Ready(false),
1457 Poll::Pending => Poll::Pending,
1458 };
1459
1460 let state = reset.store.0.concurrent_state_mut()?;
1475 let mut ready = state.switch_item.take();
1476 let mut low_priority = false;
1477 if ready.is_none() {
1478 ready = state.high_priority.pop_back();
1479 if ready.is_none() {
1480 ready = state.low_priority.pop_back();
1481 low_priority = true;
1482 }
1483 }
1484 if ready.is_some() {
1485 return Poll::Ready(Ok(PollResult::ProcessWork {
1486 ready,
1487 low_priority,
1488 }));
1489 }
1490
1491 return match next {
1495 Poll::Ready(true) => {
1496 Poll::Ready(Ok(PollResult::ProcessWork {
1502 ready: None,
1503 low_priority: false,
1504 }))
1505 }
1506 Poll::Ready(false) => {
1507 if let Poll::Ready(value) =
1511 tls::set(reset.store.0, || future.as_mut().poll(cx))
1512 {
1513 Poll::Ready(Ok(PollResult::Complete(value)))
1514 } else {
1515 if trap_on_idle {
1521 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1528 Trap::CannotBlockSyncTask.into()
1529 } else {
1530 Trap::AsyncDeadlock.into()
1532 }))
1533 } else {
1534 Poll::Pending
1538 }
1539 }
1540 }
1541 Poll::Pending => Poll::Pending,
1546 };
1547 })
1548 .await;
1549
1550 drop(reset);
1554
1555 match result? {
1556 PollResult::Complete(value) => break Ok(value),
1559 PollResult::ProcessWork {
1562 mut ready,
1563 low_priority,
1564 } => {
1565 if low_priority {
1593 if let Some(item) = ready.take() {
1594 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1595 }
1596 self.0.yield_now().await;
1597 turns_without_yield = 0;
1598 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1599 }
1600
1601 if let Some(item) = ready {
1602 self.as_context_mut().handle_work_item(item).await?;
1603 }
1604
1605 turns_without_yield += 1;
1606 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1607 turns_without_yield = 0;
1608 self.0.yield_now().await;
1609 }
1610 }
1611 }
1612 }
1613 }
1614
1615 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1617 let item = match item {
1621 WorkItem::ResumeFiber {
1622 instance,
1623 thread,
1624 fiber,
1625 } => {
1626 let future = self.0.resume_fiber(fiber);
1627 log::trace!(
1628 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1629 );
1630 return futures::future::Either::Left(future);
1631 }
1632 item => item,
1633 };
1634 futures::future::Either::Right(async move {
1635 log::trace!("handle work item {item:?}");
1636 match item {
1637 WorkItem::PushFuture(future) => {
1638 self.0
1639 .concurrent_state_mut()?
1640 .futures_mut()?
1641 .push(future.into_inner());
1642 }
1643 WorkItem::ResumeFiber { .. } => unreachable!(),
1644 WorkItem::ResumeThread { thread, .. } => {
1645 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1646 if !matches!(slot, GuestThreadState::Ready { .. }) {
1647 bail_bug!("cannot resume non-pending thread {thread:?}");
1648 }
1649 let GuestThreadState::Ready { fiber } =
1650 mem::replace(slot, GuestThreadState::Running)
1651 else {
1652 unreachable!()
1653 };
1654 self.0.resume_fiber(fiber).await?;
1655 }
1656 WorkItem::GuestCall { call, .. } => {
1657 if call.is_ready(self.0)? {
1658 self.0
1659 .concurrent_state_mut()?
1660 .get_mut(call.thread.thread)?
1661 .wake_on_cancel = WakeOnCancel::None;
1662 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1663 } else {
1664 let state = self.0.concurrent_state_mut()?;
1665 let task = state.get_mut(call.thread.task)?;
1666 if !task.starting_sent {
1667 task.starting_sent = true;
1668 if let GuestCallKind::StartImplicit(_) = &call.kind {
1669 Waitable::Guest(call.thread.task).set_event(
1670 state,
1671 Some(Event::Subtask {
1672 status: Status::Starting,
1673 }),
1674 )?;
1675 }
1676 }
1677
1678 let instance = state.get_mut(call.thread.task)?.instance;
1679 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1688 let state = self.0.concurrent_state_mut()?;
1689 if !state.get_mut(*set)?.ready.is_empty() {
1690 state.wake_waiter(*set)?;
1691 }
1692 }
1693 self.0
1694 .instance_state(instance)
1695 .concurrent_state()
1696 .pending
1697 .insert(call.thread, call.kind);
1698
1699 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1703 }
1704 }
1705 WorkItem::WorkerFunction(fun) => {
1706 self.run_on_worker(WorkerItem::Function(fun)).await?;
1707 }
1708 }
1709
1710 Ok(())
1711 })
1712 }
1713
1714 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1716 let state = self.0.concurrent_state_mut()?;
1717 if state.worker_item.is_some() {
1718 bail_bug!("worker item already set when taking worker fiber");
1719 }
1720 let worker = if let Some(fiber) = state.worker.take() {
1721 fiber
1722 } else {
1723 unsafe {
1742 fiber::make_fiber_unchecked(self.0, move |store| {
1743 loop {
1744 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1745 bail_bug!("worker_item not present when resuming fiber")
1746 };
1747 match item {
1748 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1749 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1750 }
1751
1752 store.suspend(SuspendReason::NeedWork)?;
1753 }
1754 })?
1755 }
1756 };
1757
1758 self.0
1761 .concurrent_state_mut_already_forced_current_thread()
1762 .worker_item = Some(item);
1763
1764 self.0.resume_fiber(worker).await
1765 }
1766
1767 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1772 where
1773 T: 'static,
1774 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1775 + Send
1776 + Sync
1777 + 'static,
1778 R: Send + Sync + 'static,
1779 {
1780 let token = StoreToken::new(self);
1781 async move {
1782 let mut accessor = Accessor::new(token);
1783 closure(&mut accessor).await
1784 }
1785 }
1786
1787 pub(crate) async fn start_instance(
1788 &mut self,
1789 instance: ModuleInstance,
1790 callee: Option<RuntimeInstance>,
1791 ) -> Result<ModuleInstance> {
1792 self.0.check_event_loop_not_running()?;
1794 let (tx, rx) = oneshot::channel();
1795 let token = StoreToken::new(self.as_context_mut());
1796 self.0.queue_task(move |store| {
1797 _ = tx.send(
1798 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1799 .map(|()| instance),
1800 );
1801 Ok(())
1802 })?;
1803 self.as_context_mut()
1804 .run_concurrent_trap_on_idle(async |_| {
1805 rx.await
1806 .map_err(|_| format_err!("oneshot channel canceled"))
1807 })
1808 .await??
1809 }
1810}
1811
1812pub type EnteredHostTask = Option<QualifiedThreadId>;
1819
1820impl StoreOpaque {
1821 pub(crate) fn check_event_loop_not_running(&mut self) -> Result<()> {
1822 if self
1823 .concurrent_state_mut_already_forced_current_thread()
1824 .event_loop_running
1825 {
1826 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1827 }
1828 Ok(())
1829 }
1830
1831 #[inline]
1835 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1836 if !self.concurrency_support() {
1838 return Ok(CurrentThread::None);
1839 }
1840
1841 if !self
1844 .vm_store_context_mut()
1845 .current_thread_mut()
1846 .is_deferred()
1847 {
1848 return Ok(self
1849 .concurrent_state_mut_already_forced_current_thread()
1850 .unforced_current_thread);
1851 }
1852
1853 self.force_deferred_current_thread()
1854 }
1855
1856 #[cold]
1859 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1860 let state = self.concurrent_state_mut_without_forcing_current_thread();
1869 let id = match state.unforced_current_thread.guest_task() {
1870 Some(task) => state.get_mut(task)?.instance.instance,
1871 None => bail_bug!("deferred component-model thread with non-guest base"),
1872 };
1873
1874 let mut frames = SmallVec::<[_; 8]>::new();
1877 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1878 while let Some(ptr) = cur.as_deferred() {
1879 let deferred = unsafe { ptr.as_non_null().as_ref() };
1884 frames.push((
1885 deferred.callee_async != 0,
1886 deferred.callee_instance,
1887 deferred.saved_context,
1888 deferred.lazy_lenders_start,
1889 ));
1890 cur = deferred.parent;
1891 }
1892
1893 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1897
1898 let current_context = *self.vm_store_context_mut().component_context_mut();
1901
1902 let lazy_lenders_end = *self.vm_store_context_mut().lazy_lenders_len_mut();
1909 frames.reverse();
1910 let lazy_lenders_base = match frames.first() {
1911 Some((_, _, _, start)) => *start,
1912 None => bail_bug!("forcing a deferred thread with no deferred frames"),
1913 };
1914 let lazy_lender_ends = frames
1915 .iter()
1916 .skip(1)
1917 .map(|(_, _, _, start)| *start)
1918 .chain([lazy_lenders_end])
1919 .collect::<SmallVec<[_; 8]>>();
1920 for ((callee_async, callee_instance, saved_context, start), lenders_end) in
1921 frames.into_iter().zip(lazy_lender_ends)
1922 {
1923 *self.vm_store_context_mut().component_context_mut() = saved_context;
1927 let callee = RuntimeInstance {
1928 instance: id,
1929 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1930 };
1931 self.enter_guest_sync_call(callee_async, callee)?;
1932
1933 self.promote_current_lazy_lends(id, start, lenders_end)?;
1936 }
1937
1938 debug_assert_eq!(
1941 *self.vm_store_context_mut().lazy_lenders_len_mut(),
1942 lazy_lenders_end
1943 );
1944 *self.vm_store_context_mut().lazy_lenders_len_mut() = lazy_lenders_base;
1945
1946 *self.vm_store_context_mut().component_context_mut() = current_context;
1948
1949 Ok(self
1950 .concurrent_state_mut_without_forcing_current_thread()
1951 .unforced_current_thread)
1952 }
1953
1954 fn promote_current_lazy_lends(
1960 &mut self,
1961 id: ComponentInstanceId,
1962 start: usize,
1963 end: usize,
1964 ) -> Result<()> {
1965 if start == end {
1966 return Ok(());
1967 }
1968
1969 let scope = match self
1970 .concurrent_state_mut_without_forcing_current_thread()
1971 .unforced_current_thread
1972 .guest_task()
1973 {
1974 Some(task) => VMScope::id(task.rep()),
1975 None => bail_bug!("replayed deferred frame is not a guest thread"),
1976 };
1977 let instance = Instance::from_wasmtime(self, id);
1978 self.promote_lazy_lends(instance, scope, start, end)
1979 }
1980
1981 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1982 match self.current_thread()?.guest() {
1983 Some(id) => Ok(*id),
1984 None => bail_bug!("current thread is not a guest thread"),
1985 }
1986 }
1987
1988 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1992 match self.current_thread()? {
1993 CurrentThread::Host(id) => Ok(Some(id)),
1994 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1995 _ => bail_bug!("current thread is not a host thread"),
1996 }
1997 }
1998
1999 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
2002 Ok(self
2003 .concurrent_state_mut()?
2004 .materialize_current_host_task_id()?)
2005 }
2006
2007 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
2008 log::trace!("enter sync-typed call {callee:?}");
2009 let state = self.instance_state(callee).concurrent_state();
2010 let old_do_not_suspend = state.do_not_suspend;
2011 state.do_not_suspend = true;
2012
2013 let thread = self.current_guest_thread()?;
2014 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2015 if thread.old_do_not_suspend.is_some() {
2016 bail_bug!("current thread already has `old_do_not_suspend` value");
2017 }
2018
2019 thread.old_do_not_suspend = Some(old_do_not_suspend);
2020
2021 Ok(())
2022 }
2023
2024 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
2025 log::trace!("exit sync-typed call {callee:?}");
2026 let thread = self.current_guest_thread()?;
2027 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2028 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
2029 bail_bug!("current thread missing `old_do_not_suspend` value");
2030 };
2031 let state = self.instance_state(callee).concurrent_state();
2032 state.do_not_suspend = old_do_not_suspend;
2033 Ok(())
2034 }
2035
2036 pub(crate) fn enter_guest_sync_call(
2048 &mut self,
2049 callee_async_typed: bool,
2050 callee: RuntimeInstance,
2051 ) -> Result<()> {
2052 log::trace!("enter sync-lifted call {callee:?}");
2053 if !self.concurrency_support() {
2054 return self.enter_call_not_concurrent();
2055 }
2056
2057 let state = self.concurrent_state_mut()?;
2061 let item = state.next_switch_item.take();
2062 state.saved_next_switch_items.push(item);
2063
2064 let thread = self.current_thread()?;
2065 let caller = if let Some(thread) = thread.guest() {
2066 Caller::Guest { thread: *thread }
2067 } else {
2068 Caller::Host {
2069 tx: None,
2070 caller: self.materialize_host_task_id()?,
2071 }
2072 };
2073
2074 let state = self.concurrent_state_mut()?;
2075 let guest_thread = GuestTask::new(
2076 state,
2077 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2078 LiftResult {
2079 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2080 ty: TypeTupleIndex::reserved_value(),
2081 memory: None,
2082 string_encoding: StringEncoding::Utf8,
2083 },
2084 caller,
2085 None,
2086 callee,
2087 callee_async_typed,
2088 false,
2089 )?;
2090
2091 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2092 guest_thread.thread,
2093 self,
2094 callee.index,
2095 )?;
2096 self.set_thread(guest_thread)?;
2097
2098 if !callee_async_typed {
2099 self.enter_sync_call(callee)?;
2100 }
2101
2102 Ok(())
2103 }
2104
2105 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2113 if !self.concurrency_support() {
2114 return Ok(self.exit_call_not_concurrent());
2115 }
2116
2117 let thread = match self.current_thread()?.guest() {
2118 Some(t) => *t,
2119 None => bail_bug!("expected task when exiting"),
2120 };
2121 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2122 let instance = task.instance;
2123
2124 let caller = match &task.caller {
2125 &Caller::Guest { thread } => thread.into(),
2126 &Caller::Host { caller, .. } => caller
2127 .map(CurrentThread::Host)
2128 .unwrap_or(CurrentThread::None),
2129 };
2130 task.lift_result = None;
2131 task.exited = true;
2132 let async_typed = task.async_typed;
2133
2134 if !async_typed {
2135 self.exit_sync_call(instance)?;
2136 }
2137
2138 self.set_thread(caller)?;
2139
2140 log::trace!("exit sync-lifted call {instance:?}");
2141
2142 if async_typed {
2143 self.switch_or_trap_if_may_not_suspend(instance)?;
2148 }
2149
2150 self.cleanup_thread(thread, instance)?;
2151
2152 self.concurrent_state_mut()?
2153 .decrement_task_ref_count(thread.task)?;
2154
2155 let state = self.concurrent_state_mut()?;
2156 let Some(item) = state.saved_next_switch_items.pop() else {
2157 bail_bug!("unable to pop from `saved_next_switch_items`");
2158 };
2159 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2160 state.push_high_priority(item);
2163 bail_bug!("`next_switch_item` unexpectedly already set");
2164 }
2165
2166 Ok(())
2167 }
2168
2169 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2176 if !self.concurrency_support() {
2177 self.enter_call_not_concurrent()?;
2178 return Ok(None);
2179 }
2180 let caller = self.current_guest_thread()?;
2181 log::trace!("new deferred host task with caller {caller:?}");
2182
2183 self.set_thread(CurrentThread::DeferredHost(caller))?;
2184 let state = self.concurrent_state_mut()?;
2185 debug_assert!(state.deferred_host_call_context.is_none());
2186 state.deferred_host_call_context = Some(CallContext::default());
2187 state.debug_assert_deferred_host_invariant();
2188 Ok(Some(caller))
2189 }
2190
2191 pub(crate) fn host_task_delete(
2198 &mut self,
2199 original_task: EnteredHostTask,
2200 materialized_task: Option<TableId<HostTask>>,
2201 ) -> Result<()> {
2202 match original_task {
2203 Some(caller) => {
2204 self.set_thread(caller)?;
2205 if materialized_task.is_none() {
2206 let state = self.concurrent_state_mut()?;
2207 let context = state
2208 .deferred_host_call_context
2209 .take()
2210 .expect("deferred host call context should be present");
2211 debug_assert!(context.is_empty());
2212 state.debug_assert_deferred_host_invariant();
2213 }
2214 log::trace!(
2215 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2216 );
2217 if let Some(task) = materialized_task {
2218 self.concurrent_state_mut()?.delete_host_task(task)?;
2219 }
2220 }
2221 None => {
2222 debug_assert!(materialized_task.is_none());
2223 self.exit_call_not_concurrent();
2224 }
2225 }
2226 Ok(())
2227 }
2228
2229 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2232 self.component_instance_mut(instance.instance)
2233 .instance_state(instance.index)
2234 }
2235
2236 fn instance_state_and_handle_counter(
2239 &mut self,
2240 instance: RuntimeInstance,
2241 ) -> (&mut InstanceState, &mut HandleCounter) {
2242 let (component, counter) = self
2243 .store_data_mut()
2244 .component_instance_and_handle_counter_mut(instance.instance);
2245 (component.instance_state(instance.index), counter)
2246 }
2247
2248 fn handle_table_insertion(&mut self, instance: RuntimeInstance) -> HandleTableInsertion<'_> {
2250 let (component, counter) = self
2251 .store_data_mut()
2252 .component_instance_and_handle_counter_mut(instance.instance);
2253 component.handle_table_insertion(instance.index, counter)
2254 }
2255
2256 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2262 let thread = thread.into();
2263 let state = self.concurrent_state_mut()?;
2264 state.debug_assert_deferred_host_invariant();
2265 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2266
2267 state.handle_thread_switch(old_thread, thread)?;
2268
2269 if let Some(old_thread) = old_thread.guest() {
2277 let old_context = *self.vm_store_context_mut().component_context_mut();
2278 self.concurrent_state_mut()?
2279 .get_mut(old_thread.thread)?
2280 .context = old_context;
2281 }
2282 if cfg!(debug_assertions) {
2283 *self.vm_store_context_mut().component_context_mut() =
2284 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2285 }
2286 if let Some(thread) = thread.guest() {
2287 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2288 let context = thread.context;
2289 if cfg!(debug_assertions) {
2290 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2291 }
2292 *self.vm_store_context_mut().component_context_mut() = context;
2293 }
2294
2295 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2297 VMLazyThread::none()
2298 } else {
2299 VMLazyThread::forced()
2300 };
2301
2302 Ok(old_thread)
2303 }
2304
2305 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2307 if self.switch_if_may_not_suspend(instance)? {
2308 Ok(())
2309 } else {
2310 Err(Trap::CannotBlockSyncTask.into())
2311 }
2312 }
2313
2314 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2318 self.concurrent_state_mut()?;
2322
2323 Ok(!self.concurrency_support()
2324 || !self
2325 .instance_state(instance)
2326 .concurrent_state()
2327 .do_not_suspend
2328 || self
2329 .concurrent_state_mut()?
2330 .promote_instance_local_thread_work_item(instance)?)
2331 }
2332
2333 fn enter_instance(&mut self, instance: RuntimeInstance) {
2337 log::trace!("enter {instance:?}");
2338 self.instance_state(instance)
2339 .concurrent_state()
2340 .do_not_enter = true;
2341 }
2342
2343 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2347 log::trace!("exit {instance:?}");
2348 self.instance_state(instance)
2349 .concurrent_state()
2350 .do_not_enter = false;
2351 self.partition_pending(instance)
2352 }
2353
2354 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2362 for (thread, kind) in
2363 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2364 {
2365 let call = GuestCall { thread, kind };
2366 if call.is_ready(self)? {
2367 self.concurrent_state_mut()?
2368 .push_high_priority(WorkItem::GuestCall { instance, call });
2369 } else {
2370 self.instance_state(instance)
2371 .concurrent_state()
2372 .pending
2373 .insert(call.thread, call.kind);
2374 }
2375 }
2376
2377 if let Some(waker) = self
2378 .concurrent_state_mut()?
2379 .ready_for_concurrent_call_waker
2380 .take()
2381 {
2382 waker.wake();
2383 }
2384
2385 Ok(())
2386 }
2387
2388 pub(crate) fn backpressure_modify(
2390 &mut self,
2391 caller_instance: RuntimeInstance,
2392 modify: impl FnOnce(u16) -> Option<u16>,
2393 ) -> Result<()> {
2394 let state = self.instance_state(caller_instance).concurrent_state();
2395 let old = state.backpressure;
2396 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2397 state.backpressure = new;
2398
2399 if old > 0 && new == 0 {
2400 self.partition_pending(caller_instance)?;
2403 }
2404
2405 Ok(())
2406 }
2407
2408 fn resume_fiber(
2411 &mut self,
2412 fiber: StoreFiber<'static>,
2413 ) -> impl Future<Output = Result<()>> + '_ {
2414 let mut resuming = ResumingFiber {
2415 store: self,
2416 fiber: Some(fiber),
2417 };
2418 async move {
2419 let old_thread = resuming.store.current_thread()?;
2420 log::trace!("resume_fiber: save current thread {old_thread:?}");
2421
2422 let fiber = resuming.fiber.take().unwrap();
2423 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2424
2425 resuming.store.set_thread(old_thread)?;
2426
2427 let state = resuming.store.concurrent_state_mut()?;
2428
2429 if let Some(ot) = old_thread.guest() {
2430 state.set_thread_running(ot.thread)?;
2431 }
2432 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2433
2434 if resuming.fiber.is_some() {
2435 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2436 let reason = match state.suspend_reason.take() {
2438 Some(r) => r,
2439 None => bail_bug!("suspend reason missing when resuming fiber"),
2440 };
2441 match reason {
2442 SuspendReason::NeedWork => {
2443 if state.worker.is_none() {
2444 state.worker = resuming.fiber.take();
2445 }
2446 }
2447 SuspendReason::Yielding { thread } => {
2448 let slot = &mut state.get_mut(thread.thread)?.state;
2449 slot.check_no_fiber()?;
2450 *slot = GuestThreadState::Ready {
2451 fiber: resuming.fiber.take().unwrap(),
2452 };
2453 let instance = state.get_mut(thread.task)?.instance;
2454 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2455 }
2456 SuspendReason::ExplicitlySuspending { thread } => {
2457 let slot = &mut state.get_mut(thread.thread)?.state;
2458 slot.check_no_fiber()?;
2459 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2460 }
2461 SuspendReason::Waiting { set, thread } => {
2462 match state.get_mut(set)?.waiting.entry(thread) {
2463 Entry::Vacant(entry) => {
2464 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2465 }
2466 Entry::Occupied(_) => {
2467 bail_bug!("entry unexpectedly already exists for {thread:?}");
2468 }
2469 }
2470 }
2471 SuspendReason::YieldingToSubtask { thread } => {
2472 let instance = state.get_mut(thread.task)?.instance;
2481 if state.next_switch_item.is_some() {
2482 bail_bug!(
2485 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2486 a thread wanted to wait on a subtask"
2487 );
2488 }
2489 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2490 instance,
2491 thread,
2492 fiber: resuming.fiber.take().unwrap(),
2493 });
2494 log::trace!("set next switch item to {item:?}");
2495 }
2496 };
2497 } else {
2498 log::trace!("resume_fiber: fiber has exited");
2499 }
2500
2501 Ok(())
2502 }
2503 }
2504
2505 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2511 log::trace!("suspend fiber: {reason:?}");
2512
2513 if !self.fiber_async_state_mut().can_block() {
2516 return Err(format_err!("future dropped"));
2517 }
2518
2519 let state = self.concurrent_state_mut()?;
2520
2521 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2528 SuspendReason::Yielding { .. }
2529 | SuspendReason::Waiting { .. }
2530 | SuspendReason::ExplicitlySuspending { .. } => {
2531 if state.switch_item.is_none() {
2534 state.take_next_switch_item()?;
2535 }
2536
2537 (true, false)
2538 }
2539 SuspendReason::YieldingToSubtask { .. } => (true, true),
2540 SuspendReason::NeedWork => (false, false),
2541 };
2542
2543 let old_next_switch_item = if save_and_restore_next_switch_item {
2544 Some(state.save_next_switch_item()?)
2545 } else {
2546 None
2547 };
2548
2549 let old_guest_thread = if save_and_restore_thread {
2550 self.current_thread()?
2551 } else {
2552 CurrentThread::None
2553 };
2554
2555 let waiting_set = match &reason {
2556 SuspendReason::Waiting { set, .. } => Some(*set),
2557 _ => None,
2558 };
2559
2560 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2561 if suspend_reason.is_some() {
2562 bail_bug!("suspend reason unexpectedly already set for store");
2563 }
2564 *suspend_reason = Some(reason);
2565
2566 if let Some(set) = waiting_set {
2569 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2570 }
2571
2572 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2573
2574 if let Some(set) = waiting_set {
2575 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2576 }
2577
2578 if save_and_restore_thread {
2579 self.set_thread(old_guest_thread)?;
2580 }
2581
2582 if let Some(item) = old_next_switch_item {
2583 let state = self.concurrent_state_mut()?;
2584 state.restore_next_switch_item(item)?;
2585 }
2586
2587 Ok(())
2588 }
2589
2590 fn wait_for_event(
2591 &mut self,
2592 caller_instance: RuntimeInstance,
2593 waitable: Waitable,
2594 ) -> Result<()> {
2595 let caller = self.current_guest_thread()?;
2596 let state = self.concurrent_state_mut()?;
2597
2598 waitable.trap_if_in_waitable_set(state)?;
2599
2600 let set = state.get_mut(caller.thread)?.sync_call_set;
2601 waitable.join(state, Some(set))?;
2602
2603 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2604
2605 self.suspend(SuspendReason::Waiting {
2606 set,
2607 thread: caller,
2608 })?;
2609 let state = self.concurrent_state_mut()?;
2610
2611 waitable.join(state, None)
2612 }
2613
2614 fn cleanup_thread(
2636 &mut self,
2637 guest_thread: QualifiedThreadId,
2638 runtime_instance: RuntimeInstance,
2639 ) -> Result<()> {
2640 let state = self.concurrent_state_mut()?;
2641 state.take_next_switch_item()?;
2644 let thread_data = state.get_mut(guest_thread.thread)?;
2645 let sync_call_set = thread_data.sync_call_set;
2646 if let Some(guest_id) = thread_data.instance_rep {
2647 self.instance_state(runtime_instance)
2648 .thread_handle_table()
2649 .guest_thread_remove(guest_id)?;
2650 }
2651 let state = self.concurrent_state_mut()?;
2652
2653 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2655 if let Some(Event::Subtask {
2656 status: Status::Returned | Status::ReturnCancelled,
2657 }) = waitable.common(state)?.event
2658 {
2659 waitable.delete_from(state)?;
2660 }
2661 }
2662
2663 state.delete(guest_thread.thread)?;
2664 state.delete(sync_call_set)?;
2665 let task = state.get_mut(guest_thread.task)?;
2666 task.threads.remove(&guest_thread.thread);
2667
2668 if task.threads.is_empty() && !task.returned_or_cancelled() {
2669 bail!(Trap::NoAsyncResult);
2670 }
2671
2672 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2673 task.decremented_interesting_task_count = true;
2674
2675 debug_assert!(state.interesting_tasks > 0);
2676 state.interesting_tasks -= 1;
2677 if state.interesting_tasks == 0
2678 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2679 {
2680 waker.wake();
2681 }
2682 }
2683
2684 state.decrement_task_ref_count(guest_thread.task)?;
2685
2686 Ok(())
2687 }
2688
2689 fn cancel_guest_subtask_without_lowered_parameters(
2698 &mut self,
2699 caller_instance: RuntimeInstance,
2700 guest_task: TableId<GuestTask>,
2701 ) -> Result<()> {
2702 let concurrent_state = self.concurrent_state_mut()?;
2703 let task = concurrent_state.get_mut(guest_task)?;
2704 assert!(!task.already_lowered_parameters());
2705 task.lower_params = None;
2709 task.lift_result = None;
2710 task.exited = true;
2711 let instance = task.instance;
2712
2713 assert_eq!(1, task.threads.len());
2720 let thread = *task.threads.iter().next().unwrap();
2721 let next_switch_item = concurrent_state.save_next_switch_item()?;
2722 self.cleanup_thread(
2723 QualifiedThreadId {
2724 task: guest_task,
2725 thread,
2726 },
2727 caller_instance,
2728 )?;
2729 self.concurrent_state_mut()?
2730 .restore_next_switch_item(next_switch_item)?;
2731
2732 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2734 let pending_count = pending.len();
2735 pending.retain(|thread, _| thread.task != guest_task);
2736
2737 if pending.len() == pending_count {
2738 let matches = |item: &WorkItem| match item {
2741 WorkItem::ResumeThread { thread, .. }
2742 | WorkItem::GuestCall {
2743 call: GuestCall { thread, .. },
2744 ..
2745 } => thread.task == guest_task,
2746 _ => false,
2747 };
2748
2749 let remove = |queue: &mut VecDeque<WorkItem>| {
2750 let count = queue.len();
2751 queue.retain(|item| !matches(item));
2752 count != queue.len()
2753 };
2754
2755 let state = self.concurrent_state_mut()?;
2756 let mut found = remove(&mut state.high_priority);
2757 found |= remove(&mut state.low_priority);
2758
2759 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2760 if matches!(item, Some(item) if matches(item)) {
2761 found = true;
2762 *item = None;
2763 }
2764 }
2765
2766 if !found {
2767 bail!(Trap::SubtaskCancelAfterTerminal);
2768 }
2769 }
2770 Ok(())
2771 }
2772
2773 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2776 if !self.concurrency_support() {
2777 return Ok(self
2778 .current_scope_id_not_concurrent()?
2779 .map(|id| CurrentScope::Id(VMScope::id(id))));
2780 }
2781
2782 Ok(match self.current_thread()? {
2783 CurrentThread::Guest(id) => Some(CurrentScope::Id(VMScope::id(id.task.rep()))),
2784 CurrentThread::Host(id) => Some(CurrentScope::Id(VMScope::host_id(id.rep()))),
2785 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2786 CurrentThread::None => return Ok(None),
2787 })
2788 }
2789
2790 pub(crate) fn queue_task(
2791 &mut self,
2792 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2793 ) -> Result<()> {
2794 self.concurrent_state_mut()?
2795 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2796 Ok(())
2797 }
2798
2799 fn any_may_not_suspend(&mut self) -> Result<bool> {
2808 Ok(self
2816 .concurrent_state_mut()?
2817 .table
2818 .get_mut()
2819 .iter_mut()
2820 .filter_map(|(_, entry)| {
2821 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2822 Some(task.instance)
2823 } else {
2824 None
2825 }
2826 })
2827 .collect::<Vec<_>>()
2828 .into_iter()
2829 .any(|instance| {
2830 self.instance_state(instance)
2831 .concurrent_state()
2832 .do_not_suspend
2833 }))
2834 }
2835}
2836
2837impl Instance {
2838 fn get_event(
2841 self,
2842 store: &mut StoreOpaque,
2843 guest_task: TableId<GuestTask>,
2844 set: Option<TableId<WaitableSet>>,
2845 cancellable: bool,
2846 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2847 let state = store.concurrent_state_mut()?;
2848
2849 let task = state.get_mut(guest_task)?;
2850 let event = &mut task.event;
2851 if let Some(ev) = event
2852 && (cancellable || !matches!(ev, Event::Cancelled))
2853 {
2854 log::trace!("deliver event {ev:?} to {guest_task:?}");
2855
2856 if matches!(ev, Event::Cancelled) {
2857 task.cancel_request_delivered = true;
2858 }
2859
2860 let ev = *ev;
2861 *event = None;
2862 return Ok(Some((ev, None)));
2863 }
2864
2865 let set = match set {
2866 Some(set) => set,
2867 None => return Ok(None),
2868 };
2869 let waitable = match state.get_mut(set)?.ready.pop_first() {
2870 Some(v) => v,
2871 None => return Ok(None),
2872 };
2873
2874 let common = waitable.common(state)?;
2875 let handle = match common.handle {
2876 Some(h) => h,
2877 None => bail_bug!("handle not set when delivering event"),
2878 };
2879 let event = match common.event.take() {
2880 Some(e) => e,
2881 None => bail_bug!("event not set when delivering event"),
2882 };
2883
2884 log::trace!(
2885 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2886 );
2887
2888 waitable.on_delivery(store, self, event)?;
2889
2890 Ok(Some((event, Some((waitable, handle)))))
2891 }
2892
2893 fn handle_callback_code(
2900 self,
2901 store: &mut dyn VMStore,
2902 guest_thread: QualifiedThreadId,
2903 runtime_instance: RuntimeInstance,
2904 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2905 ) -> Result<()> {
2906 let old_thread = store.set_thread(guest_thread)?;
2907 log::trace!(
2908 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2909 );
2910
2911 store.enter_instance(runtime_instance);
2912
2913 let mut code = call(store)?;
2914
2915 if matches!(
2921 unpack_callback_code(code).0,
2922 callback_code::YIELD | callback_code::WAIT
2923 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2924 {
2925 let (unpacked, set) = unpack_callback_code(code);
2926 log::trace!(
2927 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2928 cancellation; delivering it via callback"
2929 );
2930 code = invoke_callback(store, guest_thread.task, event, 0)?;
2931 }
2932
2933 store.exit_instance(runtime_instance)?;
2934
2935 store.set_thread(old_thread)?;
2936 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2937
2938 let store = store.store_opaque_mut();
2939 let (code, set) = unpack_callback_code(code);
2940
2941 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2942
2943 let state = store.concurrent_state_mut()?;
2944
2945 state.take_next_switch_item()?;
2946
2947 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2948 let set = store
2949 .instance_state(runtime_instance)
2950 .handle_table()
2951 .waitable_set_rep(handle)?;
2952
2953 Ok(TableId::<WaitableSet>::new(set))
2954 };
2955
2956 match code {
2957 callback_code::EXIT => {
2958 log::trace!("implicit thread {guest_thread:?} completed");
2959 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2960 task.exited = true;
2961 task.callback = None;
2962
2963 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2968
2969 store.cleanup_thread(guest_thread, runtime_instance)?;
2970 }
2971 callback_code::YIELD => {
2972 let old = state
2975 .get_mut(guest_thread.thread)?
2976 .wake_on_cancel
2977 .replace(WakeOnCancel::Yielding);
2978 if !old.is_none() {
2979 bail_bug!("thread unexpectedly had wake_on_cancel set");
2980 }
2981
2982 let call = GuestCall {
2989 thread: guest_thread,
2990 kind: GuestCallKind::DeliverEvent {
2991 instance: self,
2992 set: None,
2993 },
2994 };
2995 state.push_low_priority(WorkItem::GuestCall {
2998 instance: runtime_instance,
2999 call,
3000 });
3001 }
3002 callback_code::WAIT => {
3003 let set = get_set(store, set)?;
3004 self.wait_with_callback(store, guest_thread, set)?;
3005 }
3006 _ => bail!(Trap::UnsupportedCallbackCode),
3007 }
3008
3009 Ok(())
3010 }
3011
3012 fn wait_with_callback(
3018 self,
3019 store: &mut StoreOpaque,
3020 guest_thread: QualifiedThreadId,
3021 set: TableId<WaitableSet>,
3022 ) -> Result<()> {
3023 let state = store.concurrent_state_mut()?;
3024 state.get_mut(set)?.num_waiting += 1;
3027
3028 if state.get_mut(guest_thread.task)?.event.is_some()
3029 || !state.get_mut(set)?.ready.is_empty()
3030 {
3031 let instance = state.get_mut(guest_thread.task)?.instance;
3033 state.push_high_priority(WorkItem::GuestCall {
3034 instance,
3035 call: GuestCall {
3036 thread: guest_thread,
3037 kind: GuestCallKind::DeliverEvent {
3038 instance: self,
3039 set: Some(set),
3040 },
3041 },
3042 });
3043 return Ok(());
3044 }
3045
3046 let instance = store
3052 .concurrent_state_mut()?
3053 .get_mut(guest_thread.task)?
3054 .instance;
3055 store.switch_or_trap_if_may_not_suspend(instance)?;
3056
3057 let state = store.concurrent_state_mut()?;
3063 let old = state
3064 .get_mut(guest_thread.thread)?
3065 .wake_on_cancel
3066 .replace(WakeOnCancel::Waiting(set));
3067 if !old.is_none() {
3068 bail_bug!("thread unexpectedly had wake_on_cancel set");
3069 }
3070 let old = state
3071 .get_mut(set)?
3072 .waiting
3073 .insert(guest_thread, WaitMode::Callback(self));
3074 if !old.is_none() {
3075 bail_bug!("set's waiting set already had this thread registered");
3076 }
3077 Ok(())
3078 }
3079
3080 unsafe fn stage_call<T: 'static>(
3087 self,
3088 mut store: StoreContextMut<T>,
3089 guest_thread: QualifiedThreadId,
3090 callee: SendSyncPtr<VMFuncRef>,
3091 param_count: usize,
3092 result_count: usize,
3093 async_: bool,
3094 callback: Option<SendSyncPtr<VMFuncRef>>,
3095 post_return: Option<SendSyncPtr<VMFuncRef>>,
3096 host_caller: bool,
3097 ) -> Result<()> {
3098 unsafe fn make_call<T: 'static>(
3113 store: StoreContextMut<T>,
3114 guest_thread: QualifiedThreadId,
3115 callee: SendSyncPtr<VMFuncRef>,
3116 param_count: usize,
3117 result_count: usize,
3118 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3119 + Send
3120 + Sync
3121 + 'static
3122 + use<T> {
3123 let token = StoreToken::new(store);
3124 move |store: &mut dyn VMStore| {
3125 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3126
3127 store
3128 .concurrent_state_mut()?
3129 .set_thread_running(guest_thread.thread)?;
3130 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3131 let lower = match task.lower_params.take() {
3132 Some(l) => l,
3133 None => bail_bug!("lower_params missing"),
3134 };
3135
3136 lower(store, &mut storage[..param_count])?;
3137
3138 let mut store = token.as_context_mut(store);
3139
3140 unsafe {
3143 crate::Func::call_unchecked_raw(
3144 &mut store,
3145 callee.as_non_null(),
3146 NonNull::new(
3147 &mut storage[..param_count.max(result_count)]
3148 as *mut [MaybeUninit<ValRaw>] as _,
3149 )
3150 .unwrap(),
3151 UncaughtException::Trap,
3152 )?;
3153 }
3154
3155 Ok(storage)
3156 }
3157 }
3158
3159 let call = unsafe {
3163 make_call(
3164 store.as_context_mut(),
3165 guest_thread,
3166 callee,
3167 param_count,
3168 result_count,
3169 )
3170 };
3171
3172 let callee_instance = store
3173 .0
3174 .concurrent_state_mut()?
3175 .get_mut(guest_thread.task)?
3176 .instance;
3177
3178 let fun = if callback.is_some() {
3179 assert!(async_);
3180
3181 Box::new(move |store: &mut dyn VMStore| {
3182 self.add_guest_thread_to_instance_table(
3183 guest_thread.thread,
3184 store,
3185 callee_instance.index,
3186 )?;
3187 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3188 let storage = call(store)?;
3189
3190 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3193 })
3194 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3195 } else {
3196 let token = StoreToken::new(store.as_context_mut());
3197 Box::new(move |store: &mut dyn VMStore| {
3198 self.add_guest_thread_to_instance_table(
3199 guest_thread.thread,
3200 store,
3201 callee_instance.index,
3202 )?;
3203 let old_thread = store.set_thread(guest_thread)?;
3204 log::trace!(
3205 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3206 );
3207 let flags = self.id().get(store).instance_flags(callee_instance.index);
3208
3209 let callee_async_typed = store
3210 .concurrent_state_mut()?
3211 .get_mut(guest_thread.task)?
3212 .async_typed;
3213
3214 if !async_ && callee_async_typed {
3218 store.enter_instance(callee_instance);
3219 }
3220
3221 if !callee_async_typed {
3222 store.enter_sync_call(callee_instance)?;
3223 }
3224
3225 let storage = call(store)?;
3232
3233 if !callee_async_typed {
3234 store.exit_sync_call(callee_instance)?;
3235 }
3236
3237 if !async_ {
3238 if callee_async_typed {
3244 store.exit_instance(callee_instance)?;
3245 }
3246
3247 let lift = {
3248 let state = store.concurrent_state_mut()?;
3249 if !state.get_mut(guest_thread.task)?.result.is_none() {
3250 bail_bug!("task has already produced a result");
3251 }
3252
3253 match state.get_mut(guest_thread.task)?.lift_result.take() {
3254 Some(lift) => lift,
3255 None => bail_bug!("lift_result field is missing"),
3256 }
3257 };
3258
3259 let result = (lift.lift)(store, unsafe {
3262 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3263 &storage[..result_count],
3264 )
3265 })?;
3266
3267 let post_return_arg = match result_count {
3268 0 => ValRaw::i32(0),
3269 1 => unsafe { storage[0].assume_init() },
3272 _ => unreachable!(),
3273 };
3274
3275 unsafe {
3276 call_post_return(
3277 token.as_context_mut(store),
3278 post_return.map(|v| v.as_non_null()),
3279 post_return_arg,
3280 flags,
3281 )?;
3282 }
3283
3284 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3285 }
3286
3287 store.set_thread(old_thread)?;
3288
3289 store
3290 .concurrent_state_mut()?
3291 .get_mut(guest_thread.task)?
3292 .exited = true;
3293
3294 log::trace!(
3295 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3296 );
3297
3298 if callee_async_typed {
3299 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3304 }
3305
3306 store.cleanup_thread(guest_thread, callee_instance)?;
3308 Ok(())
3309 })
3310 };
3311
3312 store.0.concurrent_state_mut()?.push_work_item(
3313 WorkItem::GuestCall {
3314 instance: callee_instance,
3315 call: GuestCall {
3316 thread: guest_thread,
3317 kind: GuestCallKind::StartImplicit(fun),
3318 },
3319 },
3320 if host_caller {
3321 Priority::High
3322 } else {
3323 Priority::Switch
3324 },
3325 )?;
3326
3327 Ok(())
3328 }
3329
3330 unsafe fn prepare_call<T: 'static>(
3343 self,
3344 mut store: StoreContextMut<T>,
3345 start: NonNull<VMFuncRef>,
3346 return_: NonNull<VMFuncRef>,
3347 caller_instance: RuntimeComponentInstanceIndex,
3348 callee_instance: RuntimeComponentInstanceIndex,
3349 task_return_type: TypeTupleIndex,
3350 callee_async_typed: bool,
3351 memory: *mut VMMemoryDefinition,
3352 string_encoding: StringEncoding,
3353 caller_info: CallerInfo,
3354 ) -> Result<()> {
3355 enum ResultInfo {
3356 Heap { results: u32 },
3357 Stack { result_count: u32 },
3358 }
3359
3360 let result_info = match &caller_info {
3361 CallerInfo::Async {
3362 has_result: true,
3363 params,
3364 } => ResultInfo::Heap {
3365 results: match params.last() {
3367 Some(r) => r.get_u32(),
3368 None => bail_bug!("retptr missing"),
3369 },
3370 },
3371 CallerInfo::Async {
3372 has_result: false, ..
3373 } => ResultInfo::Stack { result_count: 0 },
3374 CallerInfo::Sync {
3375 result_count,
3376 params,
3377 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3378 results: match params.last() {
3380 Some(r) => r.get_u32(),
3381 None => bail_bug!("arg ptr missing"),
3382 },
3383 },
3384 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3385 result_count: *result_count,
3386 },
3387 };
3388
3389 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3390
3391 let start = SendSyncPtr::new(start);
3395 let return_ = SendSyncPtr::new(return_);
3396 let token = StoreToken::new(store.as_context_mut());
3397 let old_thread = store.0.current_guest_thread()?;
3398
3399 let state = store.0.concurrent_state_mut()?;
3400
3401 debug_assert_eq!(
3402 state.get_mut(old_thread.task)?.instance,
3403 self.runtime_instance(caller_instance)
3404 );
3405
3406 let guest_thread = GuestTask::new(
3407 state,
3408 Box::new(move |store, dst| {
3409 let mut store = token.as_context_mut(store);
3410 assert!(dst.len() <= MAX_FLAT_PARAMS);
3411 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3413 let count = match caller_info {
3414 CallerInfo::Async { params, has_result } => {
3418 let params = ¶ms[..params.len() - usize::from(has_result)];
3419 for (param, src) in params.iter().zip(&mut src) {
3420 src.write(*param);
3421 }
3422 params.len()
3423 }
3424
3425 CallerInfo::Sync { params, .. } => {
3427 for (param, src) in params.iter().zip(&mut src) {
3428 src.write(*param);
3429 }
3430 params.len()
3431 }
3432 };
3433 unsafe {
3440 crate::Func::call_unchecked_raw(
3441 &mut store,
3442 start.as_non_null(),
3443 NonNull::new(
3444 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3445 )
3446 .unwrap(),
3447 UncaughtException::Trap,
3448 )?;
3449 }
3450 dst.copy_from_slice(&src[..dst.len()]);
3451 let task = store.0.current_guest_thread()?.task;
3452 let state = store.0.concurrent_state_mut()?;
3453 Waitable::Guest(task).set_event(
3454 state,
3455 Some(Event::Subtask {
3456 status: Status::Started,
3457 }),
3458 )?;
3459 Ok(())
3460 }),
3461 LiftResult {
3462 lift: Box::new(move |store, src| {
3463 let mut store = token.as_context_mut(store);
3466 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3468 my_src.push(ValRaw::u32(*results));
3469 }
3470
3471 unsafe {
3478 crate::Func::call_unchecked_raw(
3479 &mut store,
3480 return_.as_non_null(),
3481 my_src.as_mut_slice().into(),
3482 UncaughtException::Trap,
3483 )?;
3484 }
3485
3486 let thread = store.0.current_guest_thread()?;
3487 let state = store.0.concurrent_state_mut()?;
3488 if sync_caller {
3489 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3490 if let ResultInfo::Stack { result_count } = &result_info {
3491 match result_count {
3492 0 => None,
3493 1 => Some(my_src[0]),
3494 _ => unreachable!(),
3495 }
3496 } else {
3497 None
3498 },
3499 );
3500 }
3501 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3502 }),
3503 ty: task_return_type,
3504 memory: NonNull::new(memory).map(SendSyncPtr::new),
3505 string_encoding,
3506 },
3507 Caller::Guest { thread: old_thread },
3508 None,
3509 self.runtime_instance(callee_instance),
3510 callee_async_typed,
3511 false,
3514 )?;
3515
3516 store.0.set_thread(guest_thread)?;
3519 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3520
3521 Ok(())
3522 }
3523
3524 unsafe fn call_callback<T>(
3529 self,
3530 mut store: StoreContextMut<T>,
3531 function: SendSyncPtr<VMFuncRef>,
3532 event: Event,
3533 handle: u32,
3534 ) -> Result<u32> {
3535 let (ordinal, result) = event.parts();
3536 let params = &mut [
3537 ValRaw::u32(ordinal),
3538 ValRaw::u32(handle),
3539 ValRaw::u32(result),
3540 ];
3541 unsafe {
3546 crate::Func::call_unchecked_raw(
3547 &mut store,
3548 function.as_non_null(),
3549 params.as_mut_slice().into(),
3550 UncaughtException::Trap,
3551 )?;
3552 }
3553 Ok(params[0].get_u32())
3554 }
3555
3556 unsafe fn start_call<T: 'static>(
3574 self,
3575 mut store: StoreContextMut<T>,
3576 callback: *mut VMFuncRef,
3577 post_return: *mut VMFuncRef,
3578 callee: NonNull<VMFuncRef>,
3579 param_count: u32,
3580 result_count: u32,
3581 flags: u32,
3582 storage: &mut [MaybeUninit<ValRaw>],
3583 ) -> Result<()> {
3584 let token = StoreToken::new(store.as_context_mut());
3585 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3586 let guest_thread = store.0.current_guest_thread()?;
3587 let state = store.0.concurrent_state_mut()?;
3588
3589 if !state.event_loop_running {
3594 bail!(
3595 "async function started during an original synchronous \
3596 entrypoint; please use an async-style entrypoint instead"
3597 );
3598 }
3599
3600 let callee = SendSyncPtr::new(callee);
3601 let param_count = usize::try_from(param_count)?;
3602 assert!(param_count <= MAX_FLAT_PARAMS);
3603 let result_count = usize::try_from(result_count)?;
3604 assert!(result_count <= MAX_FLAT_RESULTS);
3605
3606 let task = state.get_mut(guest_thread.task)?;
3607 let callee_async_typed = task.async_typed;
3608 let callee_instance = task.instance;
3609
3610 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3611
3612 if let Some(callback) = NonNull::new(callback) {
3613 let callback = SendSyncPtr::new(callback);
3617 task.callback = Some(Box::new(move |store, event, handle| {
3618 let store = token.as_context_mut(store);
3619 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3620 }));
3621 }
3622
3623 let Caller::Guest { thread: caller } = &task.caller else {
3624 bail_bug!("start_call unexpectedly invoked for host->guest call");
3627 };
3628 let caller = *caller;
3629 let caller_instance = state.get_mut(caller.task)?.instance;
3630
3631 unsafe {
3633 self.stage_call(
3634 store.as_context_mut(),
3635 guest_thread,
3636 callee,
3637 param_count,
3638 result_count,
3639 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3640 NonNull::new(callback).map(SendSyncPtr::new),
3641 NonNull::new(post_return).map(SendSyncPtr::new),
3642 false,
3643 )?;
3644 }
3645
3646 let old_do_not_suspend = if callee_async_typed {
3647 let state = store.0.instance_state(callee_instance).concurrent_state();
3654 let old_do_not_suspend = state.do_not_suspend;
3655 state.do_not_suspend = false;
3656 Some(old_do_not_suspend)
3657 } else {
3658 None
3659 };
3660
3661 let state = store.0.concurrent_state_mut()?;
3662
3663 let guest_waitable = Waitable::Guest(guest_thread.task);
3666 let old_set = guest_waitable.common(state)?.set;
3667 let set = state.get_mut(caller.thread)?.sync_call_set;
3668 guest_waitable.join(state, Some(set))?;
3669
3670 store.0.set_thread(CurrentThread::None)?;
3671
3672 let mut yielded = false;
3688 let (status, waitable) = loop {
3689 store.0.suspend(if yielded {
3690 SuspendReason::Waiting {
3691 set,
3692 thread: caller,
3693 }
3694 } else {
3695 yielded = true;
3696 SuspendReason::YieldingToSubtask { thread: caller }
3697 })?;
3698
3699 if let Some(old_do_not_suspend) = old_do_not_suspend {
3700 store
3701 .0
3702 .instance_state(callee_instance)
3703 .concurrent_state()
3704 .do_not_suspend = old_do_not_suspend;
3705 }
3706
3707 let state = store.0.concurrent_state_mut()?;
3708
3709 log::trace!("taking event for {:?}", guest_thread.task);
3710 let event = guest_waitable.take_event(state)?;
3711 let Some(Event::Subtask { status }) = event else {
3712 bail_bug!("subtasks should only get subtask events, got {event:?}")
3713 };
3714
3715 log::trace!("status {status:?} for {:?}", guest_thread.task);
3716
3717 if status == Status::Returned {
3718 break (status, None);
3720 } else if async_caller {
3721 let handle = store
3725 .0
3726 .handle_table_insertion(caller_instance)
3727 .subtask_guest(guest_thread.task.rep())?;
3728 store
3729 .0
3730 .concurrent_state_mut()?
3731 .get_mut(guest_thread.task)?
3732 .common
3733 .handle = Some(handle);
3734 break (status, Some(handle));
3735 } else {
3736 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3740 }
3741 };
3742
3743 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3744
3745 store.0.set_thread(caller)?;
3747
3748 store
3749 .0
3750 .concurrent_state_mut()?
3751 .set_thread_running(caller.thread)?;
3752
3753 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3754
3755 if async_caller {
3756 let Some(slot) = storage.first_mut() else {
3757 bail_bug!("no storage for async call status");
3758 };
3759 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3760 } else {
3761 let state = store.0.concurrent_state_mut()?;
3764 let task = state.get_mut(guest_thread.task)?;
3765 if let Some(Some(result)) = task.sync_result.take()? {
3766 storage[0] = MaybeUninit::new(result);
3767 }
3768 }
3769
3770 if status == Status::Returned {
3771 store
3772 .0
3773 .concurrent_state_mut()?
3774 .decrement_task_ref_count(guest_thread.task)?;
3775 }
3776
3777 Ok(())
3778 }
3779
3780 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3796 self,
3797 mut store: StoreContextMut<'_, T>,
3798 host_task: EnteredHostTask,
3799 result_may_require_realloc: bool,
3800 future: impl Future<Output = Result<R>> + Send + 'static,
3801 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3802 + Send
3803 + 'static,
3804 ) -> Result<u32> {
3805 let token = StoreToken::new(store.as_context_mut());
3806
3807 let (join_handle, future) = JoinHandle::run(future);
3810 let mut future = Box::pin(future);
3811
3812 let poll = tls::set(store.0, || {
3817 future
3818 .as_mut()
3819 .poll(&mut Context::from_waker(&Waker::noop()))
3820 });
3821
3822 match poll {
3823 Poll::Ready(result) => {
3825 let result = result.transpose()?;
3826 let task = store.0.current_materialized_host_task()?;
3829 lower(store.as_context_mut(), result, true, task)?;
3830 return Ok(Status::Returned.pack(None));
3831 }
3832
3833 Poll::Pending => {}
3835 }
3836
3837 let Some(task) = store.0.materialize_host_task_id()? else {
3841 bail_bug!("current thread is not a host thread")
3842 };
3843 {
3844 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3845 assert!(matches!(state, HostTaskState::CalleeStarted));
3846 *state = HostTaskState::CalleeRunning(join_handle);
3847 }
3848
3849 let future = Box::pin(async move {
3857 let result = match run_with_host_task_set(task, future).await? {
3858 Some(result) => Some(result?),
3859 None => None,
3860 };
3861 let on_complete = move |store: &mut dyn VMStore| {
3862 let mut store = token.as_context_mut(store);
3866 let old = store.0.set_thread(task)?;
3867
3868 let status = if result.is_some() {
3869 Status::Returned
3870 } else {
3871 Status::ReturnCancelled
3872 };
3873
3874 lower(store.as_context_mut(), result, false, Some(task))?;
3875 let state = store.0.concurrent_state_mut()?;
3876 match &mut state.get_mut(task)?.state {
3877 pending @ HostTaskState::CalleeCancelling => {
3880 *pending = HostTaskState::CalleeDone { cancelled: true };
3881 }
3882
3883 other => *other = HostTaskState::CalleeDone { cancelled: false },
3885 }
3886 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3887
3888 store.0.set_thread(old)?;
3889 Ok(())
3890 };
3891
3892 tls::get(move |store| {
3893 if result_may_require_realloc {
3894 store
3899 .concurrent_state_mut()?
3900 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3901 on_complete,
3902 ))));
3903 Ok(())
3904 } else {
3905 on_complete(store)
3908 }
3909 })
3910 });
3911
3912 let caller = match host_task {
3915 Some(caller) => caller,
3916 None => bail_bug!("host task wasn't created but should have been"),
3917 };
3918 let state = store.0.concurrent_state_mut()?;
3919 state.push_future(future);
3920 let instance = state.get_mut(caller.task)?.instance;
3921 let handle = store
3922 .0
3923 .handle_table_insertion(instance)
3924 .subtask_host(task.rep())?;
3925 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3926 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3927
3928 store.0.set_thread(caller)?;
3932 Ok(Status::Started.pack(Some(handle)))
3933 }
3934
3935 pub(crate) fn task_return(
3938 self,
3939 store: &mut dyn VMStore,
3940 ty: TypeTupleIndex,
3941 options: OptionsIndex,
3942 storage: &[ValRaw],
3943 ) -> Result<()> {
3944 let guest_thread = store.current_guest_thread()?;
3945 let state = store.concurrent_state_mut()?;
3946 if !state.get_mut(guest_thread.task)?.async_lifted {
3947 bail!(Trap::TaskReturnOrCancelSyncLifted);
3948 }
3949 let lift = state
3950 .get_mut(guest_thread.task)?
3951 .lift_result
3952 .take()
3953 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3954 if !state.get_mut(guest_thread.task)?.result.is_none() {
3955 bail_bug!("task result unexpectedly already set");
3956 }
3957
3958 let CanonicalOptions {
3959 string_encoding,
3960 data_model,
3961 ..
3962 } = &self.id().get(store).component().env_component().options[options];
3963
3964 let invalid = ty != lift.ty
3965 || string_encoding != &lift.string_encoding
3966 || match data_model {
3967 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3968 Some(memory) => {
3969 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3970 let actual = self.id().get(store).runtime_memory(memory);
3971 expected != actual.as_ptr()
3972 }
3973 None => false,
3976 },
3977 CanonicalOptionsDataModel::Gc { .. } => true,
3979 };
3980
3981 if invalid {
3982 bail!(Trap::TaskReturnInvalid);
3983 }
3984
3985 log::trace!("task.return for {guest_thread:?}");
3986
3987 let result = (lift.lift)(store, storage)?;
3988 self.task_complete(store, guest_thread.task, result, Status::Returned)
3989 }
3990
3991 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3993 let guest_thread = store.current_guest_thread()?;
3994 let state = store.concurrent_state_mut()?;
3995 let task = state.get_mut(guest_thread.task)?;
3996 if !task.async_lifted {
3997 bail!(Trap::TaskReturnOrCancelSyncLifted);
3998 }
3999 if !task.cancel_request_delivered {
4000 bail!(Trap::TaskCancelNotCancelled);
4001 }
4002 _ = task
4003 .lift_result
4004 .take()
4005 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
4006
4007 if !task.result.is_none() {
4008 bail_bug!("task result should not bet set yet");
4009 }
4010
4011 log::trace!("task.cancel for {guest_thread:?}");
4012
4013 self.task_complete(
4014 store,
4015 guest_thread.task,
4016 Box::new(DummyResult),
4017 Status::ReturnCancelled,
4018 )
4019 }
4020
4021 fn task_complete(
4027 self,
4028 store: &mut StoreOpaque,
4029 guest_task: TableId<GuestTask>,
4030 result: Box<dyn Any + Send + Sync>,
4031 status: Status,
4032 ) -> Result<()> {
4033 store
4034 .component_resource_tables(Some(self))?
4035 .validate_scope_exit()?;
4036
4037 let state = store.concurrent_state_mut()?;
4038 let task = state.get_mut(guest_task)?;
4039
4040 task.event = None;
4044
4045 if let Caller::Host { tx, .. } = &mut task.caller {
4046 if let Some(tx) = tx.take() {
4047 _ = tx.send(result);
4048 }
4049 } else {
4050 task.result = Some(result);
4051 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
4052 }
4053
4054 Ok(())
4055 }
4056
4057 pub(crate) fn waitable_set_new(
4059 self,
4060 store: &mut StoreOpaque,
4061 caller_instance: RuntimeComponentInstanceIndex,
4062 ) -> Result<u32> {
4063 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
4064 let handle = store
4065 .handle_table_insertion(self.runtime_instance(caller_instance))
4066 .waitable_set(set.rep())?;
4067 log::trace!("new waitable set {set:?} (handle {handle})");
4068 Ok(handle)
4069 }
4070
4071 pub(crate) fn waitable_set_drop(
4073 self,
4074 store: &mut StoreOpaque,
4075 caller_instance: RuntimeComponentInstanceIndex,
4076 set: u32,
4077 ) -> Result<()> {
4078 let rep = store
4079 .instance_state(self.runtime_instance(caller_instance))
4080 .handle_table()
4081 .waitable_set_remove(set)?;
4082
4083 log::trace!("drop waitable set {rep} (handle {set})");
4084
4085 let set = store
4089 .concurrent_state_mut()?
4090 .get_mut(TableId::<WaitableSet>::new(rep))?;
4091 if set.num_waiting > 0 {
4092 bail!(Trap::WaitableSetDropHasWaiters);
4093 }
4094
4095 store
4096 .concurrent_state_mut()?
4097 .delete(TableId::<WaitableSet>::new(rep))?;
4098
4099 Ok(())
4100 }
4101
4102 pub(crate) fn waitable_join(
4104 self,
4105 store: &mut StoreOpaque,
4106 caller_instance: RuntimeComponentInstanceIndex,
4107 waitable_handle: u32,
4108 set_handle: u32,
4109 ) -> Result<()> {
4110 let mut instance = self.id().get_mut(store);
4111 let waitable =
4112 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4113
4114 let set = if set_handle == 0 {
4115 None
4116 } else {
4117 let set = instance.instance_states().0[caller_instance]
4118 .handle_table()
4119 .waitable_set_rep(set_handle)?;
4120
4121 Some(TableId::<WaitableSet>::new(set))
4122 };
4123
4124 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4125
4126 log::trace!(
4127 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4128 );
4129
4130 waitable.join(store.concurrent_state_mut()?, set)
4131 }
4132
4133 pub(crate) fn subtask_drop(
4135 self,
4136 store: &mut StoreOpaque,
4137 caller_instance: RuntimeComponentInstanceIndex,
4138 task_id: u32,
4139 ) -> Result<()> {
4140 self.waitable_join(store, caller_instance, task_id, 0)?;
4141
4142 let (rep, is_host) = store
4143 .instance_state(self.runtime_instance(caller_instance))
4144 .handle_table()
4145 .subtask_remove(task_id)?;
4146
4147 let concurrent_state = store.concurrent_state_mut()?;
4148 let waitable = if is_host {
4149 let id = TableId::<HostTask>::new(rep);
4150 let task = concurrent_state.get_mut(id)?;
4151 match &task.state {
4152 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4153 bail!(Trap::SubtaskDropNotResolved)
4154 }
4155 HostTaskState::CalleeDone { .. } => {}
4156 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4157 bail_bug!("invalid state for callee in `subtask.drop`")
4158 }
4159 }
4160
4161 Waitable::Host(id)
4162 } else {
4163 let id = TableId::<GuestTask>::new(rep);
4164 let task = concurrent_state.get_mut(id)?;
4165 if task.lift_result.is_some() {
4166 bail!(Trap::SubtaskDropNotResolved);
4167 }
4168 Waitable::Guest(id)
4169 };
4170
4171 waitable.common(concurrent_state)?.handle = None;
4172
4173 if waitable.take_event(concurrent_state)?.is_some() {
4176 bail!(Trap::SubtaskDropNotResolved);
4177 }
4178
4179 waitable.delete_from(concurrent_state)?;
4180
4181 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4182 Ok(())
4183 }
4184
4185 pub(crate) fn waitable_set_wait(
4187 self,
4188 store: &mut StoreOpaque,
4189 options: OptionsIndex,
4190 set: u32,
4191 payload: u32,
4192 ) -> Result<u32> {
4193 let &CanonicalOptions {
4194 instance: caller_instance,
4195 ..
4196 } = &self.id().get(store).component().env_component().options[options];
4197 let caller = self.runtime_instance(caller_instance);
4198 let rep = store
4199 .instance_state(self.runtime_instance(caller_instance))
4200 .handle_table()
4201 .waitable_set_rep(set)?;
4202
4203 self.waitable_check(
4204 store,
4205 caller,
4206 WaitableCheck::Wait,
4207 WaitableCheckParams {
4208 set: TableId::new(rep),
4209 options,
4210 payload,
4211 },
4212 )
4213 }
4214
4215 pub(crate) fn waitable_set_poll(
4217 self,
4218 store: &mut StoreOpaque,
4219 options: OptionsIndex,
4220 set: u32,
4221 payload: u32,
4222 ) -> Result<u32> {
4223 let &CanonicalOptions {
4224 instance: caller_instance,
4225 ..
4226 } = &self.id().get(store).component().env_component().options[options];
4227 let caller = self.runtime_instance(caller_instance);
4228 let rep = store
4229 .instance_state(caller)
4230 .handle_table()
4231 .waitable_set_rep(set)?;
4232
4233 self.waitable_check(
4234 store,
4235 caller,
4236 WaitableCheck::Poll,
4237 WaitableCheckParams {
4238 set: TableId::new(rep),
4239 options,
4240 payload,
4241 },
4242 )
4243 }
4244
4245 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4247 let thread_id = store.current_guest_thread()?.thread;
4248 match store
4249 .concurrent_state_mut()?
4250 .get_mut(thread_id)?
4251 .instance_rep
4252 {
4253 Some(r) => Ok(r),
4254 None => bail_bug!("thread should have instance_rep by now"),
4255 }
4256 }
4257
4258 pub(crate) fn thread_new_indirect<T: 'static>(
4260 self,
4261 mut store: StoreContextMut<T>,
4262 runtime_instance: RuntimeComponentInstanceIndex,
4263 start_func_ty: ModuleInternedTypeIndex,
4264 start_func_table_idx: RuntimeTableIndex,
4265 start_func_idx: u32,
4266 context: i32,
4267 ) -> Result<u32> {
4268 log::trace!("creating new thread");
4269
4270 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4271 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4272 else {
4273 bail_bug!("thread.new-indirect start function type should be registered");
4274 };
4275 let callee = instance
4276 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4277 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4278 if callee.type_index(store.0) != start_func_ty {
4279 bail!(Trap::ThreadNewIndirectInvalidType);
4280 }
4281
4282 let token = StoreToken::new(store.as_context_mut());
4283 let start_func = Box::new(
4284 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4285 let old_thread = store.set_thread(guest_thread)?;
4286 log::trace!(
4287 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4288 );
4289
4290 let mut store = token.as_context_mut(store);
4291 let mut params = [ValRaw::i32(context)];
4292 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4295
4296 store.0.set_thread(old_thread)?;
4297
4298 let runtime_instance = self.runtime_instance(runtime_instance);
4299
4300 store
4303 .0
4304 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4305
4306 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4307
4308 log::trace!("explicit thread {guest_thread:?} completed");
4309 let state = store.0.concurrent_state_mut()?;
4310 if let Some(t) = old_thread.guest() {
4311 state.set_thread_running(t.thread)?;
4312 }
4313 log::trace!("thread start: restored {old_thread:?} as current thread");
4314
4315 Ok(())
4316 },
4317 );
4318
4319 let current_thread = store.0.current_guest_thread()?;
4320 let state = store.0.concurrent_state_mut()?;
4321 let parent_task = current_thread.task;
4322
4323 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4324 let thread_id = state.push(new_thread)?;
4325 state.get_mut(parent_task)?.threads.insert(thread_id);
4326
4327 log::trace!("new thread with id {thread_id:?} created");
4328
4329 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4330 }
4331
4332 pub(crate) fn resume_thread(
4333 self,
4334 store: &mut StoreOpaque,
4335 runtime_instance: RuntimeComponentInstanceIndex,
4336 thread_idx: u32,
4337 how: ResumeThread,
4338 ) -> Result<bool> {
4339 let thread_id =
4340 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4341 let state = store.concurrent_state_mut()?;
4342 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4343
4344 if store.current_guest_thread()? == guest_thread {
4345 bail!(Trap::CannotResumeThread);
4346 }
4347
4348 let priority = match how {
4349 ResumeThread::Resume => Priority::Switch,
4350 ResumeThread::ResumeLater => Priority::Low,
4351
4352 ResumeThread::Promote => {
4355 let instance = self.runtime_instance(runtime_instance);
4356 let do_not_enter = store
4357 .instance_state(instance)
4358 .concurrent_state()
4359 .do_not_enter;
4360 return store.concurrent_state_mut()?.promote_work_item_matching(
4361 |item: &WorkItem| match item {
4362 WorkItem::ResumeThread { thread, .. }
4363 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4364
4365 WorkItem::GuestCall {
4366 call: GuestCall { thread, kind },
4367 ..
4368 } => {
4369 *thread == guest_thread
4370 && match kind {
4371 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4375
4376 GuestCallKind::StartExplicit(_) => true,
4379
4380 GuestCallKind::StartImplicit(_) => false,
4384 }
4385 }
4386
4387 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4388 },
4389 );
4390 }
4391 };
4392
4393 let state = store.concurrent_state_mut()?;
4394 let thread = state.get_mut(guest_thread.thread)?;
4395
4396 match &thread.state {
4399 GuestThreadState::NotStartedExplicit(_) => {}
4400 GuestThreadState::Suspended(_) => {
4401 log::trace!("resuming thread {thread_id:?} that was suspended");
4402 state.schedule_suspended_thread(
4403 guest_thread,
4404 self.runtime_instance(runtime_instance),
4405 priority,
4406 )?;
4407 return Ok(true);
4408 }
4409 _ => bail!(Trap::CannotResumeThread),
4410 }
4411 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4412 GuestThreadState::NotStartedExplicit(start_func) => {
4413 log::trace!("starting thread {guest_thread:?}");
4414 let guest_call = WorkItem::GuestCall {
4415 instance: self.runtime_instance(runtime_instance),
4416 call: GuestCall {
4417 thread: guest_thread,
4418 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4419 start_func(store, guest_thread)
4420 })),
4421 },
4422 };
4423 state.push_work_item(guest_call, priority)?;
4424 }
4425 other => {
4426 thread.state = other;
4427 bail_bug!("thread state checked to be resumable above");
4428 }
4429 }
4430 Ok(true)
4431 }
4432
4433 fn add_guest_thread_to_instance_table(
4434 self,
4435 thread_id: TableId<GuestThread>,
4436 store: &mut StoreOpaque,
4437 runtime_instance: RuntimeComponentInstanceIndex,
4438 ) -> Result<u32> {
4439 let (state, counter) =
4440 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4441 let guest_id = state
4442 .thread_handle_table()
4443 .guest_thread_insert(counter, thread_id.rep())?;
4444 store
4445 .concurrent_state_mut()?
4446 .get_mut(thread_id)?
4447 .instance_rep = Some(guest_id);
4448 Ok(guest_id)
4449 }
4450
4451 pub(crate) fn suspension_intrinsic(
4455 self,
4456 store: &mut StoreOpaque,
4457 caller: RuntimeComponentInstanceIndex,
4458 yielding: bool,
4459 to_thread: SuspensionTarget,
4460 ) -> Result<WaitResult> {
4461 let check_suspend = match to_thread {
4462 SuspensionTarget::Promote(thread) => {
4463 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4464 }
4465 SuspensionTarget::Resume(thread) => {
4466 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4467 bail_bug!(
4468 "`resume_thread` should only ever return false \
4469 when `ResumeThread::Promote` is passed to it"
4470 );
4471 }
4472 false
4473 }
4474 SuspensionTarget::None => true,
4475 };
4476
4477 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4478 return if yielding {
4479 Ok(WaitResult::Completed)
4480 } else {
4481 Err(Trap::CannotBlockSyncTask.into())
4482 };
4483 }
4484
4485 let guest_thread = store.current_guest_thread()?;
4486
4487 let reason = if yielding {
4488 SuspendReason::Yielding {
4489 thread: guest_thread,
4490 }
4491 } else {
4492 SuspendReason::ExplicitlySuspending {
4493 thread: guest_thread,
4494 }
4495 };
4496
4497 store.suspend(reason)?;
4498
4499 Ok(WaitResult::Completed)
4500 }
4501
4502 fn waitable_check(
4504 self,
4505 store: &mut StoreOpaque,
4506 caller: RuntimeInstance,
4507 check: WaitableCheck,
4508 params: WaitableCheckParams,
4509 ) -> Result<u32> {
4510 let guest_thread = store.current_guest_thread()?;
4511
4512 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4513
4514 match &check {
4517 WaitableCheck::Wait => {
4518 let set = params.set;
4519
4520 loop {
4525 let state = store.concurrent_state_mut()?;
4526 let task = state.get_mut(guest_thread.task)?;
4527 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4528 || !state.get_mut(set)?.ready.is_empty()
4529 {
4530 break;
4531 }
4532
4533 store.switch_or_trap_if_may_not_suspend(caller)?;
4534
4535 store.suspend(SuspendReason::Waiting {
4536 set,
4537 thread: guest_thread,
4538 })?;
4539 }
4540 }
4541 WaitableCheck::Poll => {}
4542 }
4543
4544 log::trace!(
4545 "waitable check for {guest_thread:?}; set {:?}, part two",
4546 params.set
4547 );
4548
4549 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4551
4552 let (ordinal, handle, result) = match &check {
4553 WaitableCheck::Wait => {
4554 let (event, waitable) = match event {
4555 Some(p) => p,
4556 None => bail_bug!("event expected to be present"),
4557 };
4558 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4559 let (ordinal, result) = event.parts();
4560 (ordinal, handle, result)
4561 }
4562 WaitableCheck::Poll => {
4563 if let Some((event, waitable)) = event {
4564 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4565 let (ordinal, result) = event.parts();
4566 (ordinal, handle, result)
4567 } else {
4568 log::trace!(
4569 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4570 guest_thread.task,
4571 params.set
4572 );
4573 let (ordinal, result) = Event::None.parts();
4574 (ordinal, 0, result)
4575 }
4576 }
4577 };
4578 let ptr = crate::component::func::validate_inbounds_dynamic(
4579 &CanonicalAbiInfo::POINTER_PAIR,
4580 self.options_memory(store, params.options),
4581 &ValRaw::u32(params.payload),
4582 )?;
4583 let memory = self
4584 .options_memory_range_mut(store, params.options, ptr, 8)
4585 .unwrap();
4586 memory[0..4].copy_from_slice(&handle.to_le_bytes());
4587 memory[4..8].copy_from_slice(&result.to_le_bytes());
4588 Ok(ordinal)
4589 }
4590
4591 pub(crate) fn subtask_cancel(
4593 self,
4594 store: &mut StoreOpaque,
4595 caller_instance: RuntimeComponentInstanceIndex,
4596 async_: bool,
4597 task_id: u32,
4598 ) -> Result<u32> {
4599 let (rep, is_host) = store
4600 .instance_state(self.runtime_instance(caller_instance))
4601 .handle_table()
4602 .subtask_rep(task_id)?;
4603 let waitable = if is_host {
4604 Waitable::Host(TableId::<HostTask>::new(rep))
4605 } else {
4606 Waitable::Guest(TableId::<GuestTask>::new(rep))
4607 };
4608 let concurrent_state = store.concurrent_state_mut()?;
4609
4610 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4611
4612 waitable.trap_if_in_waitable_set(concurrent_state)?;
4613
4614 let needs_block;
4615 if let Waitable::Host(host_task) = waitable {
4616 let state = &mut concurrent_state.get_mut(host_task)?.state;
4617 match state {
4618 HostTaskState::CalleeRunning(handle) => {
4625 handle.abort();
4626 *state = HostTaskState::CalleeCancelling;
4627 needs_block = true;
4628 }
4629
4630 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4633 bail!(Trap::SubtaskCancelAfterTerminal);
4634 }
4635 HostTaskState::CalleeDone { cancelled: false } => {
4636 *state = HostTaskState::CalleeDone { cancelled: true };
4639 needs_block = false;
4640 }
4641
4642 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4645 bail_bug!("invalid states for host callee")
4646 }
4647 }
4648 } else {
4649 let guest_task = TableId::<GuestTask>::new(rep);
4650 let task = concurrent_state.get_mut(guest_task)?;
4651
4652 if task.cancel_requested {
4655 bail!(Trap::SubtaskCancelAfterTerminal);
4656 }
4657
4658 if !task.already_lowered_parameters() {
4659 store.cancel_guest_subtask_without_lowered_parameters(
4660 self.runtime_instance(caller_instance),
4661 guest_task,
4662 )?;
4663 return Ok(Status::StartCancelled as u32);
4664 } else if !task.returned_or_cancelled() {
4665 task.cancel_requested = true;
4672 task.event = Some(Event::Cancelled);
4673 let runtime_instance = task.instance;
4674 for thread in task.threads.clone() {
4675 let thread = QualifiedThreadId {
4676 task: guest_task,
4677 thread,
4678 };
4679 let concurrent_state = store.concurrent_state_mut()?;
4680 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4681
4682 let yield_ = |store: &mut StoreOpaque| {
4683 let state = store.instance_state(runtime_instance).concurrent_state();
4688 let old_do_not_suspend = state.do_not_suspend;
4689 state.do_not_suspend = false;
4690
4691 let caller = store.current_guest_thread()?;
4692
4693 let state = store.concurrent_state_mut()?;
4698 let set = state.get_mut(caller.thread)?.sync_call_set;
4699 waitable.join(state, Some(set))?;
4700
4701 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4702
4703 let state = store.concurrent_state_mut()?;
4704 waitable.join(state, None)?;
4705
4706 store
4707 .instance_state(runtime_instance)
4708 .concurrent_state()
4709 .do_not_suspend = old_do_not_suspend;
4710
4711 Ok::<(), crate::Error>(())
4712 };
4713
4714 match thread_mut.wake_on_cancel.take() {
4715 WakeOnCancel::Waiting(set) => {
4716 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4718 let item = WorkItem::GuestCall {
4719 instance: runtime_instance,
4720 call: GuestCall {
4721 thread,
4722 kind: GuestCallKind::DeliverEvent {
4723 instance,
4724 set: Some(set),
4725 },
4726 },
4727 };
4728 concurrent_state.set_switch_item(item)?;
4729
4730 yield_(store)?;
4731
4732 break;
4733 }
4734 WakeOnCancel::Yielding => {
4735 if concurrent_state.promote_thread_work_item(thread)? {
4736 yield_(store)?;
4737 break;
4738 } else if store
4739 .instance_state(runtime_instance)
4740 .concurrent_state()
4741 .pending
4742 .contains_key(&thread)
4743 {
4744 store
4754 .concurrent_state_mut()?
4755 .get_mut(thread.thread)?
4756 .wake_on_cancel = WakeOnCancel::Yielding;
4757 } else {
4758 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4759 }
4760 }
4761 WakeOnCancel::None => {}
4762 }
4763 }
4764
4765 needs_block = !store
4768 .concurrent_state_mut()?
4769 .get_mut(guest_task)?
4770 .returned_or_cancelled()
4771 } else {
4772 needs_block = false;
4773 }
4774 };
4775
4776 if needs_block {
4780 if async_ {
4781 return Ok(BLOCKED);
4782 }
4783
4784 let old_next_switch_item = if is_host {
4794 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4795 } else {
4796 None
4797 };
4798
4799 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4802
4803 if let Some(item) = old_next_switch_item {
4804 let state = store.concurrent_state_mut()?;
4805 state.restore_next_switch_item(item)?;
4806 }
4807
4808 }
4810
4811 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4812 if let Some(Event::Subtask {
4813 status: status @ (Status::Returned | Status::ReturnCancelled),
4814 }) = event
4815 {
4816 Ok(status as u32)
4817 } else {
4818 bail!(Trap::SubtaskCancelAfterTerminal);
4819 }
4820 }
4821}
4822
4823pub trait VMComponentAsyncStore {
4831 unsafe fn prepare_call(
4837 &mut self,
4838 instance: Instance,
4839 memory: *mut VMMemoryDefinition,
4840 start: NonNull<VMFuncRef>,
4841 return_: NonNull<VMFuncRef>,
4842 caller_instance: RuntimeComponentInstanceIndex,
4843 callee_instance: RuntimeComponentInstanceIndex,
4844 task_return_type: TypeTupleIndex,
4845 callee_async: bool,
4846 string_encoding: StringEncoding,
4847 result_count: u32,
4848 storage: *mut ValRaw,
4849 storage_len: usize,
4850 ) -> Result<()>;
4851
4852 unsafe fn start_call(
4855 &mut self,
4856 instance: Instance,
4857 callback: *mut VMFuncRef,
4858 post_return: *mut VMFuncRef,
4859 callee: NonNull<VMFuncRef>,
4860 param_count: u32,
4861 result_count: u32,
4862 flags: u32,
4863 storage: *mut MaybeUninit<ValRaw>,
4864 storage_len: usize,
4865 ) -> Result<()>;
4866
4867 fn future_write(
4869 &mut self,
4870 instance: Instance,
4871 caller: RuntimeComponentInstanceIndex,
4872 ty: TypeFutureTableIndex,
4873 options: OptionsIndex,
4874 future: u32,
4875 address: u32,
4876 ) -> Result<u32>;
4877
4878 fn future_read(
4880 &mut self,
4881 instance: Instance,
4882 caller: RuntimeComponentInstanceIndex,
4883 ty: TypeFutureTableIndex,
4884 options: OptionsIndex,
4885 future: u32,
4886 address: u32,
4887 ) -> Result<u32>;
4888
4889 fn future_drop_writable(
4891 &mut self,
4892 instance: Instance,
4893 ty: TypeFutureTableIndex,
4894 writer: u32,
4895 ) -> Result<()>;
4896
4897 fn stream_write(
4899 &mut self,
4900 instance: Instance,
4901 caller: RuntimeComponentInstanceIndex,
4902 ty: TypeStreamTableIndex,
4903 options: OptionsIndex,
4904 stream: u32,
4905 address: u32,
4906 count: u32,
4907 ) -> Result<u32>;
4908
4909 fn stream_read(
4911 &mut self,
4912 instance: Instance,
4913 caller: RuntimeComponentInstanceIndex,
4914 ty: TypeStreamTableIndex,
4915 options: OptionsIndex,
4916 stream: u32,
4917 address: u32,
4918 count: u32,
4919 ) -> Result<u32>;
4920
4921 fn flat_stream_write(
4924 &mut self,
4925 instance: Instance,
4926 caller: RuntimeComponentInstanceIndex,
4927 ty: TypeStreamTableIndex,
4928 options: OptionsIndex,
4929 payload_size: u32,
4930 payload_align: u32,
4931 stream: u32,
4932 address: u32,
4933 count: u32,
4934 ) -> Result<u32>;
4935
4936 fn flat_stream_read(
4939 &mut self,
4940 instance: Instance,
4941 caller: RuntimeComponentInstanceIndex,
4942 ty: TypeStreamTableIndex,
4943 options: OptionsIndex,
4944 payload_size: u32,
4945 payload_align: u32,
4946 stream: u32,
4947 address: u32,
4948 count: u32,
4949 ) -> Result<u32>;
4950
4951 fn stream_drop_writable(
4953 &mut self,
4954 instance: Instance,
4955 ty: TypeStreamTableIndex,
4956 writer: u32,
4957 ) -> Result<()>;
4958
4959 fn error_context_debug_message(
4961 &mut self,
4962 instance: Instance,
4963 ty: TypeComponentLocalErrorContextTableIndex,
4964 options: OptionsIndex,
4965 err_ctx_handle: u32,
4966 debug_msg_address: u32,
4967 ) -> Result<()>;
4968
4969 fn thread_new_indirect(
4971 &mut self,
4972 instance: Instance,
4973 caller: RuntimeComponentInstanceIndex,
4974 func_ty_idx: ModuleInternedTypeIndex,
4975 start_func_table_idx: RuntimeTableIndex,
4976 start_func_idx: u32,
4977 context: i32,
4978 ) -> Result<u32>;
4979}
4980
4981impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4983 unsafe fn prepare_call(
4984 &mut self,
4985 instance: Instance,
4986 memory: *mut VMMemoryDefinition,
4987 start: NonNull<VMFuncRef>,
4988 return_: NonNull<VMFuncRef>,
4989 caller_instance: RuntimeComponentInstanceIndex,
4990 callee_instance: RuntimeComponentInstanceIndex,
4991 task_return_type: TypeTupleIndex,
4992 callee_async: bool,
4993 string_encoding: StringEncoding,
4994 result_count_or_max_if_async: u32,
4995 storage: *mut ValRaw,
4996 storage_len: usize,
4997 ) -> Result<()> {
4998 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
5002
5003 unsafe {
5004 instance.prepare_call(
5005 StoreContextMut(self),
5006 start,
5007 return_,
5008 caller_instance,
5009 callee_instance,
5010 task_return_type,
5011 callee_async,
5012 memory,
5013 string_encoding,
5014 match result_count_or_max_if_async {
5015 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
5016 params,
5017 has_result: false,
5018 },
5019 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
5020 params,
5021 has_result: true,
5022 },
5023 result_count => CallerInfo::Sync {
5024 params,
5025 result_count,
5026 },
5027 },
5028 )
5029 }
5030 }
5031
5032 unsafe fn start_call(
5033 &mut self,
5034 instance: Instance,
5035 callback: *mut VMFuncRef,
5036 post_return: *mut VMFuncRef,
5037 callee: NonNull<VMFuncRef>,
5038 param_count: u32,
5039 result_count: u32,
5040 flags: u32,
5041 storage: *mut MaybeUninit<ValRaw>,
5042 storage_len: usize,
5043 ) -> Result<()> {
5044 unsafe {
5045 instance.start_call(
5046 StoreContextMut(self),
5047 callback,
5048 post_return,
5049 callee,
5050 param_count,
5051 result_count,
5052 flags,
5053 core::slice::from_raw_parts_mut(storage, storage_len),
5057 )
5058 }
5059 }
5060
5061 fn future_write(
5062 &mut self,
5063 instance: Instance,
5064 caller: RuntimeComponentInstanceIndex,
5065 ty: TypeFutureTableIndex,
5066 options: OptionsIndex,
5067 future: u32,
5068 address: u32,
5069 ) -> Result<u32> {
5070 instance
5071 .guest_write(
5072 StoreContextMut(self),
5073 caller,
5074 TransmitIndex::Future(ty),
5075 options,
5076 None,
5077 future,
5078 address,
5079 1,
5080 )
5081 .map(|result| result.encode())
5082 }
5083
5084 fn future_read(
5085 &mut self,
5086 instance: Instance,
5087 caller: RuntimeComponentInstanceIndex,
5088 ty: TypeFutureTableIndex,
5089 options: OptionsIndex,
5090 future: u32,
5091 address: u32,
5092 ) -> Result<u32> {
5093 instance
5094 .guest_read(
5095 StoreContextMut(self),
5096 caller,
5097 TransmitIndex::Future(ty),
5098 options,
5099 None,
5100 future,
5101 address,
5102 1,
5103 )
5104 .map(|result| result.encode())
5105 }
5106
5107 fn stream_write(
5108 &mut self,
5109 instance: Instance,
5110 caller: RuntimeComponentInstanceIndex,
5111 ty: TypeStreamTableIndex,
5112 options: OptionsIndex,
5113 stream: u32,
5114 address: u32,
5115 count: u32,
5116 ) -> Result<u32> {
5117 instance
5118 .guest_write(
5119 StoreContextMut(self),
5120 caller,
5121 TransmitIndex::Stream(ty),
5122 options,
5123 None,
5124 stream,
5125 address,
5126 count,
5127 )
5128 .map(|result| result.encode())
5129 }
5130
5131 fn stream_read(
5132 &mut self,
5133 instance: Instance,
5134 caller: RuntimeComponentInstanceIndex,
5135 ty: TypeStreamTableIndex,
5136 options: OptionsIndex,
5137 stream: u32,
5138 address: u32,
5139 count: u32,
5140 ) -> Result<u32> {
5141 instance
5142 .guest_read(
5143 StoreContextMut(self),
5144 caller,
5145 TransmitIndex::Stream(ty),
5146 options,
5147 None,
5148 stream,
5149 address,
5150 count,
5151 )
5152 .map(|result| result.encode())
5153 }
5154
5155 fn future_drop_writable(
5156 &mut self,
5157 instance: Instance,
5158 ty: TypeFutureTableIndex,
5159 writer: u32,
5160 ) -> Result<()> {
5161 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5162 }
5163
5164 fn flat_stream_write(
5165 &mut self,
5166 instance: Instance,
5167 caller: RuntimeComponentInstanceIndex,
5168 ty: TypeStreamTableIndex,
5169 options: OptionsIndex,
5170 payload_size: u32,
5171 payload_align: u32,
5172 stream: u32,
5173 address: u32,
5174 count: u32,
5175 ) -> Result<u32> {
5176 instance
5177 .guest_write(
5178 StoreContextMut(self),
5179 caller,
5180 TransmitIndex::Stream(ty),
5181 options,
5182 Some(FlatAbi {
5183 size: payload_size,
5184 align: payload_align,
5185 }),
5186 stream,
5187 address,
5188 count,
5189 )
5190 .map(|result| result.encode())
5191 }
5192
5193 fn flat_stream_read(
5194 &mut self,
5195 instance: Instance,
5196 caller: RuntimeComponentInstanceIndex,
5197 ty: TypeStreamTableIndex,
5198 options: OptionsIndex,
5199 payload_size: u32,
5200 payload_align: u32,
5201 stream: u32,
5202 address: u32,
5203 count: u32,
5204 ) -> Result<u32> {
5205 instance
5206 .guest_read(
5207 StoreContextMut(self),
5208 caller,
5209 TransmitIndex::Stream(ty),
5210 options,
5211 Some(FlatAbi {
5212 size: payload_size,
5213 align: payload_align,
5214 }),
5215 stream,
5216 address,
5217 count,
5218 )
5219 .map(|result| result.encode())
5220 }
5221
5222 fn stream_drop_writable(
5223 &mut self,
5224 instance: Instance,
5225 ty: TypeStreamTableIndex,
5226 writer: u32,
5227 ) -> Result<()> {
5228 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5229 }
5230
5231 fn error_context_debug_message(
5232 &mut self,
5233 instance: Instance,
5234 ty: TypeComponentLocalErrorContextTableIndex,
5235 options: OptionsIndex,
5236 err_ctx_handle: u32,
5237 debug_msg_address: u32,
5238 ) -> Result<()> {
5239 instance.error_context_debug_message(
5240 StoreContextMut(self),
5241 ty,
5242 options,
5243 err_ctx_handle,
5244 debug_msg_address,
5245 )
5246 }
5247
5248 fn thread_new_indirect(
5249 &mut self,
5250 instance: Instance,
5251 caller: RuntimeComponentInstanceIndex,
5252 func_ty_idx: ModuleInternedTypeIndex,
5253 start_func_table_idx: RuntimeTableIndex,
5254 start_func_idx: u32,
5255 context: i32,
5256 ) -> Result<u32> {
5257 instance.thread_new_indirect(
5258 StoreContextMut(self),
5259 caller,
5260 func_ty_idx,
5261 start_func_table_idx,
5262 start_func_idx,
5263 context,
5264 )
5265 }
5266}
5267
5268type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5269
5270async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5273where
5274 F: Future,
5275{
5276 let mut future = pin!(future);
5277 future::poll_fn(|cx| {
5278 let old_thread = match tls::get(|store| store.set_thread(task)) {
5279 Ok(thread) => thread,
5280 Err(error) => return Poll::Ready(Err(error)),
5281 };
5282 let result = future.as_mut().poll(cx);
5283 match tls::get(|store| store.set_thread(old_thread)) {
5284 Ok(_) => result.map(Ok),
5285 Err(error) => Poll::Ready(Err(error)),
5286 }
5287 })
5288 .await
5289}
5290
5291pub(crate) struct HostTask {
5295 common: WaitableCommon,
5296
5297 call_context: CallContext,
5300
5301 state: HostTaskState,
5302
5303 group: TaskGroupId,
5304}
5305
5306enum HostTaskState {
5307 CalleeStarted,
5312
5313 CalleeRunning(JoinHandle),
5318
5319 CalleeCancelling,
5323
5324 CalleeFinished(LiftedResult),
5328
5329 CalleeDone { cancelled: bool },
5332}
5333
5334impl HostTask {
5335 fn new(
5336 concurrent_state: &mut ConcurrentState,
5337 state: HostTaskState,
5338 caller: QualifiedThreadId,
5339 ) -> Result<Self> {
5340 let group = concurrent_state.get_mut(caller.task)?.group;
5341 concurrent_state.increment_group_ref_count(group)?;
5342
5343 Ok(Self {
5344 common: WaitableCommon::default(),
5345 call_context: CallContext::default(),
5346 state,
5347 group,
5348 })
5349 }
5350}
5351
5352impl TableDebug for HostTask {
5353 fn type_name() -> &'static str {
5354 "HostTask"
5355 }
5356}
5357
5358type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5359
5360enum Caller {
5362 Host {
5364 tx: Option<oneshot::Sender<LiftedResult>>,
5366 caller: Option<TableId<HostTask>>,
5370 },
5371 Guest {
5373 thread: QualifiedThreadId,
5375 },
5376}
5377
5378struct LiftResult {
5381 lift: RawLift,
5382 ty: TypeTupleIndex,
5383 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5384 string_encoding: StringEncoding,
5385}
5386
5387#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5392pub(crate) struct QualifiedThreadId {
5393 task: TableId<GuestTask>,
5394 thread: TableId<GuestThread>,
5395}
5396
5397impl QualifiedThreadId {
5398 fn qualify(
5399 state: &mut ConcurrentState,
5400 thread: TableId<GuestThread>,
5401 ) -> Result<QualifiedThreadId> {
5402 Ok(QualifiedThreadId {
5403 task: state.get_mut(thread)?.parent_task,
5404 thread,
5405 })
5406 }
5407}
5408
5409impl fmt::Debug for QualifiedThreadId {
5410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5411 f.debug_tuple("QualifiedThreadId")
5412 .field(&self.task.rep())
5413 .field(&self.thread.rep())
5414 .finish()
5415 }
5416}
5417
5418enum GuestThreadState {
5419 NotStartedImplicit,
5420 NotStartedExplicit(
5421 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5422 ),
5423 Running,
5424 Suspended(StoreFiber<'static>),
5425 Ready {
5426 fiber: StoreFiber<'static>,
5427 },
5428 Completed,
5429}
5430
5431impl GuestThreadState {
5432 fn check_no_fiber(&self) -> Result<()> {
5433 match self {
5434 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5435 Self::NotStartedImplicit
5436 | Self::NotStartedExplicit(_)
5437 | Self::Running
5438 | Self::Completed => Ok(()),
5439 }
5440 }
5441}
5442
5443impl fmt::Debug for GuestThreadState {
5444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5445 match self {
5446 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5447 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5448 Self::Running => f.debug_tuple("Running").finish(),
5449 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5450 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5451 Self::Completed => f.debug_tuple("Completed").finish(),
5452 }
5453 }
5454}
5455
5456#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5457enum WakeOnCancel {
5458 None,
5459 Waiting(TableId<WaitableSet>),
5460 Yielding,
5461}
5462
5463impl WakeOnCancel {
5464 fn is_none(self) -> bool {
5465 matches!(self, WakeOnCancel::None)
5466 }
5467
5468 fn replace(&mut self, other: WakeOnCancel) -> Self {
5469 let old = *self;
5470 *self = other;
5471 old
5472 }
5473
5474 fn take(&mut self) -> Self {
5475 self.replace(WakeOnCancel::None)
5476 }
5477}
5478
5479pub struct GuestThread {
5480 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5483 parent_task: TableId<GuestTask>,
5485 wake_on_cancel: WakeOnCancel,
5488 state: GuestThreadState,
5490 instance_rep: Option<u32>,
5493 sync_call_set: TableId<WaitableSet>,
5495 old_do_not_suspend: Option<bool>,
5498}
5499
5500impl GuestThread {
5501 fn from_instance(
5504 state: Pin<&mut ComponentInstance>,
5505 caller_instance: RuntimeComponentInstanceIndex,
5506 guest_thread: u32,
5507 ) -> Result<TableId<Self>> {
5508 let rep = state.instance_states().0[caller_instance]
5509 .thread_handle_table()
5510 .guest_thread_rep(guest_thread)?;
5511 Ok(TableId::new(rep))
5512 }
5513
5514 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5515 let sync_call_set = state.push(WaitableSet {
5516 is_sync_call_set: true,
5517 ..WaitableSet::default()
5518 })?;
5519 Ok(Self {
5520 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5521 parent_task,
5522 wake_on_cancel: WakeOnCancel::None,
5523 state: GuestThreadState::NotStartedImplicit,
5524 instance_rep: None,
5525 sync_call_set,
5526 old_do_not_suspend: None,
5527 })
5528 }
5529
5530 fn new_explicit(
5531 state: &mut ConcurrentState,
5532 parent_task: TableId<GuestTask>,
5533 start_func: Box<
5534 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5535 >,
5536 ) -> Result<Self> {
5537 state.increment_task_ref_count(parent_task)?;
5538 let sync_call_set = state.push(WaitableSet {
5539 is_sync_call_set: true,
5540 ..WaitableSet::default()
5541 })?;
5542 Ok(Self {
5543 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5544 parent_task,
5545 wake_on_cancel: WakeOnCancel::None,
5546 state: GuestThreadState::NotStartedExplicit(start_func),
5547 instance_rep: None,
5548 sync_call_set,
5549 old_do_not_suspend: None,
5550 })
5551 }
5552}
5553
5554impl TableDebug for GuestThread {
5555 fn type_name() -> &'static str {
5556 "GuestThread"
5557 }
5558}
5559
5560enum SyncResult {
5561 NotProduced,
5562 Produced(Option<ValRaw>),
5563 Taken,
5564}
5565
5566impl SyncResult {
5567 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5568 Ok(match mem::replace(self, SyncResult::Taken) {
5569 SyncResult::NotProduced => None,
5570 SyncResult::Produced(val) => Some(val),
5571 SyncResult::Taken => {
5572 bail_bug!("attempted to take a synchronous result that was already taken")
5573 }
5574 })
5575 }
5576}
5577
5578pub(crate) struct GuestTask {
5580 common: WaitableCommon,
5582 lower_params: Option<RawLower>,
5584 lift_result: Option<LiftResult>,
5586 result: Option<LiftedResult>,
5589 callback: Option<CallbackFn>,
5592 caller: Caller,
5594 call_context: CallContext,
5599 sync_result: SyncResult,
5602 cancel_request_delivered: bool,
5606 cancel_requested: bool,
5611 starting_sent: bool,
5614 instance: RuntimeInstance,
5621 event: Option<Event>,
5623 exited: bool,
5625 threads: HashSet<TableId<GuestThread>>,
5627 async_typed: bool,
5630 async_lifted: bool,
5633 decremented_interesting_task_count: bool,
5636 group: TaskGroupId,
5638 ref_count: usize,
5643}
5644
5645impl GuestTask {
5646 fn already_lowered_parameters(&self) -> bool {
5647 self.lower_params.is_none()
5649 }
5650
5651 fn returned_or_cancelled(&self) -> bool {
5652 self.lift_result.is_none()
5654 }
5655
5656 fn new(
5657 state: &mut ConcurrentState,
5658 lower_params: RawLower,
5659 lift_result: LiftResult,
5660 caller: Caller,
5661 callback: Option<CallbackFn>,
5662 instance: RuntimeInstance,
5663 async_typed: bool,
5664 async_lifted: bool,
5665 ) -> Result<QualifiedThreadId> {
5666 let group = match caller {
5667 Caller::Guest { thread } => {
5668 let group = state.get_mut(thread.task)?.group;
5669 state.increment_group_ref_count(group)?;
5670 group
5671 }
5672 Caller::Host { .. } => state.make_task_group()?,
5673 };
5674
5675 let task = state.push(Self {
5676 common: WaitableCommon::default(),
5677 lower_params: Some(lower_params),
5678 lift_result: Some(lift_result),
5679 result: None,
5680 callback,
5681 caller,
5682 call_context: CallContext::default(),
5683 sync_result: SyncResult::NotProduced,
5684 cancel_request_delivered: false,
5685 cancel_requested: false,
5686 starting_sent: false,
5687 instance,
5688 event: None,
5689 exited: false,
5690 threads: HashSet::new(),
5691 async_typed,
5692 async_lifted,
5693 decremented_interesting_task_count: false,
5694 group,
5695 ref_count: 2,
5698 })?;
5699 let new_thread = GuestThread::new_implicit(state, task)?;
5700 let thread = state.push(new_thread)?;
5701 state.get_mut(task)?.threads.insert(thread);
5702 state.interesting_tasks += 1;
5703 let thread = QualifiedThreadId { task, thread };
5704 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5705 Ok(thread)
5706 }
5707}
5708
5709impl TableDebug for GuestTask {
5710 fn type_name() -> &'static str {
5711 "GuestTask"
5712 }
5713}
5714
5715#[derive(Default)]
5717struct WaitableCommon {
5718 event: Option<Event>,
5720 set: Option<TableId<WaitableSet>>,
5722 handle: Option<u32>,
5724}
5725
5726#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5728enum Waitable {
5729 Host(TableId<HostTask>),
5731 Guest(TableId<GuestTask>),
5733 Transmit(TableId<TransmitHandle>),
5735}
5736
5737impl Waitable {
5738 fn from_instance(
5741 state: Pin<&mut ComponentInstance>,
5742 caller_instance: RuntimeComponentInstanceIndex,
5743 waitable: u32,
5744 ) -> Result<Self> {
5745 use crate::runtime::vm::component::Waitable;
5746
5747 let (waitable, kind) = state.instance_states().0[caller_instance]
5748 .handle_table()
5749 .waitable_rep(waitable)?;
5750
5751 Ok(match kind {
5752 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5753 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5754 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5755 })
5756 }
5757
5758 fn rep(&self) -> u32 {
5760 match self {
5761 Self::Host(id) => id.rep(),
5762 Self::Guest(id) => id.rep(),
5763 Self::Transmit(id) => id.rep(),
5764 }
5765 }
5766
5767 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5771 log::trace!("waitable {self:?} join set {set:?}");
5772
5773 let old = mem::replace(&mut self.common(state)?.set, set);
5774
5775 if let Some(old) = old {
5776 match *self {
5777 Waitable::Host(id) => state.remove_child(id, old),
5778 Waitable::Guest(id) => state.remove_child(id, old),
5779 Waitable::Transmit(id) => state.remove_child(id, old),
5780 }?;
5781
5782 state.get_mut(old)?.ready.remove(self);
5783 }
5784
5785 if let Some(set) = set {
5786 match *self {
5787 Waitable::Host(id) => state.add_child(id, set),
5788 Waitable::Guest(id) => state.add_child(id, set),
5789 Waitable::Transmit(id) => state.add_child(id, set),
5790 }?;
5791
5792 if self.common(state)?.event.is_some() {
5793 self.mark_ready(state)?;
5794 }
5795 }
5796
5797 Ok(())
5798 }
5799
5800 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5802 Ok(match self {
5803 Self::Host(id) => &mut state.get_mut(*id)?.common,
5804 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5805 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5806 })
5807 }
5808
5809 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5815 if self.common(state)?.set.is_some() {
5816 bail!(Trap::WaitableSyncAndAsync);
5817 }
5818 Ok(())
5819 }
5820
5821 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5827 if let Some(set) = self.common(state)?.set
5828 && state.get_mut(set)?.is_sync_call_set
5829 {
5830 bail!(Trap::WaitableSyncAndAsync);
5831 }
5832 Ok(())
5833 }
5834
5835 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5839 log::trace!("set event for {self:?}: {event:?}");
5840 self.common(state)?.event = event;
5841 self.mark_ready(state)
5842 }
5843
5844 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5846 let common = self.common(state)?;
5847 let event = common.event.take();
5848 if let Some(set) = self.common(state)?.set {
5849 state.get_mut(set)?.ready.remove(self);
5850 }
5851
5852 Ok(event)
5853 }
5854
5855 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5859 if let Some(set) = self.common(state)?.set {
5860 state.get_mut(set)?.ready.insert(*self);
5861 state.wake_waiter(set)?;
5862 }
5863 Ok(())
5864 }
5865
5866 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5869 match self {
5870 Self::Host(task) => state.delete_host_task(*task),
5871 Self::Guest(task) => state.decrement_task_ref_count(*task),
5872 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5873 }
5874 }
5875}
5876
5877impl fmt::Debug for Waitable {
5878 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5879 match self {
5880 Self::Host(id) => write!(f, "{id:?}"),
5881 Self::Guest(id) => write!(f, "{id:?}"),
5882 Self::Transmit(id) => write!(f, "{id:?}"),
5883 }
5884 }
5885}
5886
5887#[derive(Default)]
5889struct WaitableSet {
5890 ready: BTreeSet<Waitable>,
5892 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5894 num_waiting: usize,
5897 is_sync_call_set: bool,
5900}
5901
5902impl WaitableSet {
5903 fn stop_waiting(&mut self) -> Result<()> {
5905 self.num_waiting = match self.num_waiting.checked_sub(1) {
5906 Some(n) => n,
5907 None => bail_bug!("waiter not accounted for in waitable set"),
5908 };
5909 Ok(())
5910 }
5911}
5912
5913impl TableDebug for WaitableSet {
5914 fn type_name() -> &'static str {
5915 "WaitableSet"
5916 }
5917}
5918
5919type RawLower =
5921 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5922
5923type RawLift = Box<
5925 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5926>;
5927
5928type LiftedResult = Box<dyn Any + Send + Sync>;
5932
5933struct DummyResult;
5936
5937#[derive(Default)]
5939pub struct ConcurrentInstanceState {
5940 backpressure: u16,
5942 do_not_enter: bool,
5944 do_not_suspend: bool,
5947 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5950}
5951
5952impl ConcurrentInstanceState {
5953 pub fn pending_is_empty(&self) -> bool {
5954 self.pending.is_empty()
5955 }
5956}
5957
5958#[derive(Debug, Copy, Clone)]
5959pub(crate) enum CurrentThread {
5960 Guest(QualifiedThreadId),
5963 Host(TableId<HostTask>),
5965 DeferredHost(QualifiedThreadId),
5968 None,
5971}
5972
5973impl CurrentThread {
5974 fn guest(&self) -> Option<&QualifiedThreadId> {
5975 match self {
5976 Self::Guest(id) => Some(id),
5977 _ => None,
5978 }
5979 }
5980
5981 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5982 match self {
5983 Self::Guest(id) => Some(id.task),
5984 _ => None,
5985 }
5986 }
5987
5988 fn is_none(&self) -> bool {
5989 matches!(self, Self::None)
5990 }
5991}
5992
5993impl From<QualifiedThreadId> for CurrentThread {
5994 fn from(id: QualifiedThreadId) -> Self {
5995 Self::Guest(id)
5996 }
5997}
5998
5999impl From<TableId<HostTask>> for CurrentThread {
6000 fn from(id: TableId<HostTask>) -> Self {
6001 Self::Host(id)
6002 }
6003}
6004
6005enum Priority {
6006 Switch,
6007 High,
6008 Low,
6009}
6010
6011pub struct ConcurrentState {
6013 unforced_current_thread: CurrentThread,
6019
6020 deferred_host_call_context: Option<CallContext>,
6026
6027 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
6032 table: AlwaysMut<ResourceTable>,
6034 switch_item: Option<WorkItem>,
6042 next_switch_item: Option<WorkItem>,
6048 high_priority: VecDeque<WorkItem>,
6050 low_priority: VecDeque<WorkItem>,
6052 suspend_reason: Option<SuspendReason>,
6056 worker: Option<StoreFiber<'static>>,
6060 worker_item: Option<WorkerItem>,
6062
6063 global_error_context_ref_counts:
6076 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
6077
6078 interesting_tasks: usize,
6091
6092 interesting_tasks_empty_waker: Option<Waker>,
6096
6097 ready_for_concurrent_call_waker: Option<Waker>,
6102
6103 event_loop_running: bool,
6105
6106 saved_next_switch_items: Vec<Option<WorkItem>>,
6109
6110 #[cfg(feature = "task-group-hook")]
6112 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6113}
6114
6115impl Default for ConcurrentState {
6116 fn default() -> Self {
6117 Self {
6118 unforced_current_thread: CurrentThread::None,
6119 deferred_host_call_context: None,
6120 table: AlwaysMut::new(ResourceTable::new()),
6121 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6122 switch_item: None,
6123 next_switch_item: None,
6124 high_priority: VecDeque::new(),
6125 low_priority: VecDeque::new(),
6126 suspend_reason: None,
6127 worker: None,
6128 worker_item: None,
6129 global_error_context_ref_counts: BTreeMap::new(),
6130 interesting_tasks: 0,
6131 interesting_tasks_empty_waker: None,
6132 ready_for_concurrent_call_waker: None,
6133 event_loop_running: false,
6134 saved_next_switch_items: Vec::new(),
6135 #[cfg(feature = "task-group-hook")]
6136 task_group_hook: None,
6137 }
6138 }
6139}
6140
6141impl ConcurrentState {
6142 pub(crate) fn take_fibers_and_futures(
6159 &mut self,
6160 fibers: &mut Vec<StoreFiber<'static>>,
6161 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6162 ) {
6163 let ConcurrentState {
6164 table,
6165 worker,
6166 switch_item,
6167 next_switch_item,
6168 high_priority,
6169 low_priority,
6170 saved_next_switch_items,
6171 futures: pending_futures,
6172 worker_item: _,
6173 unforced_current_thread: _,
6174 deferred_host_call_context: _,
6175 suspend_reason: _,
6176 global_error_context_ref_counts: _,
6177 interesting_tasks: _,
6178 interesting_tasks_empty_waker: _,
6179 ready_for_concurrent_call_waker: _,
6180 event_loop_running: _,
6181 #[cfg(feature = "task-group-hook")]
6182 task_group_hook: _,
6183 } = self;
6184
6185 let mut items = Vec::new();
6186 for (_, entry) in table.get_mut().iter_mut() {
6187 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6188 for mode in mem::take(&mut set.waiting).into_values() {
6189 match mode {
6190 WaitMode::Fiber(fiber) => {
6191 fibers.push(fiber);
6192 }
6193 WaitMode::Callback(_) => {}
6194 }
6195 }
6196 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6197 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6198 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6199 fibers.push(fiber);
6200 }
6201 GuestThreadState::NotStartedImplicit
6202 | GuestThreadState::NotStartedExplicit(_)
6203 | GuestThreadState::Running
6204 | GuestThreadState::Completed => {}
6205 }
6206 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6207 if let Some(item) = item.take() {
6208 items.push(item);
6209 }
6210 }
6211 }
6212
6213 if let Some(fiber) = worker.take() {
6214 fibers.push(fiber);
6215 }
6216
6217 let mut handle_item = |item| match item {
6218 WorkItem::ResumeFiber { fiber, .. } => {
6219 fibers.push(fiber);
6220 }
6221 WorkItem::PushFuture(future) => {
6222 pending_futures
6223 .get_mut()
6224 .as_mut()
6225 .unwrap()
6226 .push(future.into_inner());
6227 }
6228 WorkItem::ResumeThread { .. }
6229 | WorkItem::GuestCall { .. }
6230 | WorkItem::WorkerFunction(_) => {}
6231 };
6232
6233 for item in items {
6234 handle_item(item);
6235 }
6236 if let Some(item) = switch_item.take() {
6237 handle_item(item);
6238 }
6239 if let Some(item) = next_switch_item.take() {
6240 handle_item(item);
6241 }
6242 for item in mem::take(high_priority) {
6243 handle_item(item);
6244 }
6245 for item in mem::take(low_priority) {
6246 handle_item(item);
6247 }
6248 for item in mem::take(saved_next_switch_items)
6249 .into_iter()
6250 .filter_map(|v| v)
6251 {
6252 handle_item(item);
6253 }
6254
6255 if let Some(them) = pending_futures.get_mut().take() {
6256 futures.push(them);
6257 }
6258 }
6259
6260 #[cfg(feature = "gc")]
6261 pub(crate) fn trace_fiber_roots(
6262 &mut self,
6263 modules: &ModuleRegistry,
6264 unwind: &dyn Unwind,
6265 gc_roots_list: &mut GcRootsList,
6266 ) {
6267 let ConcurrentState {
6268 table,
6269 worker,
6270 switch_item,
6271 next_switch_item,
6272 high_priority,
6273 low_priority,
6274 saved_next_switch_items,
6275
6276 futures: _,
6280
6281 worker_item: _,
6283 unforced_current_thread: _,
6284 deferred_host_call_context: _,
6285 suspend_reason: _,
6286 global_error_context_ref_counts: _,
6287 interesting_tasks: _,
6288 interesting_tasks_empty_waker: _,
6289 ready_for_concurrent_call_waker: _,
6290 event_loop_running: _,
6291 #[cfg(feature = "task-group-hook")]
6292 task_group_hook: _,
6293 } = self;
6294
6295 for (_, entry) in table.get_mut().iter_mut() {
6296 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6297 for mode in set.waiting.values_mut() {
6298 match mode {
6299 WaitMode::Fiber(fiber) => {
6300 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6301 }
6302 WaitMode::Callback(_) => {}
6303 }
6304 }
6305 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6306 match &mut thread.state {
6307 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6308 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6309 }
6310 GuestThreadState::NotStartedImplicit
6311 | GuestThreadState::NotStartedExplicit(_)
6312 | GuestThreadState::Running
6313 | GuestThreadState::Completed => {}
6314 }
6315 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6316 entry.downcast_mut::<Option<WorkItem>>()
6317 {
6318 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6319 }
6320 }
6321
6322 if let Some(fiber) = worker {
6323 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6324 }
6325
6326 let mut handle_item = |item: &mut WorkItem| match item {
6327 WorkItem::ResumeFiber { fiber, .. } => {
6328 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6329 }
6330 WorkItem::PushFuture(_future) => {
6331 }
6334 WorkItem::ResumeThread { .. }
6335 | WorkItem::GuestCall { .. }
6336 | WorkItem::WorkerFunction(_) => {}
6337 };
6338
6339 if let Some(item) = switch_item {
6340 handle_item(item);
6341 }
6342 if let Some(item) = next_switch_item {
6343 handle_item(item);
6344 }
6345 for item in high_priority {
6346 handle_item(item);
6347 }
6348 for item in low_priority {
6349 handle_item(item);
6350 }
6351 for item in saved_next_switch_items
6352 .iter_mut()
6353 .filter_map(|v| v.as_mut())
6354 {
6355 handle_item(item);
6356 }
6357 }
6358
6359 fn schedule_suspended_thread(
6360 &mut self,
6361 thread: QualifiedThreadId,
6362 instance: RuntimeInstance,
6363 priority: Priority,
6364 ) -> Result<()> {
6365 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6367 bail_bug!("switch item already set");
6368 }
6369 let slot = &mut self.get_mut(thread.thread)?.state;
6370 if !matches!(slot, GuestThreadState::Suspended(_)) {
6371 bail!(Trap::CannotResumeThread);
6372 }
6373 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6374 else {
6375 unreachable!()
6376 };
6377 let item = WorkItem::ResumeFiber {
6378 instance,
6379 thread,
6380 fiber,
6381 };
6382 match priority {
6383 Priority::Switch => self.switch_item = Some(item),
6384 Priority::High => self.push_high_priority(item),
6385 Priority::Low => self.push_low_priority(item),
6386 }
6387 Ok(())
6388 }
6389
6390 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6391 let slot = &mut self.get_mut(thread)?.state;
6392 slot.check_no_fiber()?;
6393 *slot = GuestThreadState::Running;
6394 Ok(())
6395 }
6396
6397 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6398 let saved = self.push(None::<WorkItem>)?;
6401 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6402 *slot = self.next_switch_item.take();
6403 Ok(saved)
6404 }
6405
6406 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6407 if self.next_switch_item.is_some() {
6408 bail_bug!("next switch item already set when restoring");
6409 }
6410 self.next_switch_item = self.delete(saved)?;
6411 Ok(())
6412 }
6413
6414 fn take_callback_waiter(
6415 &mut self,
6416 set: TableId<WaitableSet>,
6417 thread: QualifiedThreadId,
6418 ) -> Result<Instance> {
6419 let waiting = &mut self.get_mut(set)?.waiting;
6420 let instance = match waiting.get(&thread) {
6421 Some(WaitMode::Callback(instance)) => *instance,
6422 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6423 };
6424 waiting.remove(&thread);
6425 Ok(instance)
6426 }
6427
6428 fn push<V: Send + Sync + 'static>(
6429 &mut self,
6430 value: V,
6431 ) -> Result<TableId<V>, ResourceTableError> {
6432 self.table.get_mut().push(value).map(TableId::from)
6433 }
6434
6435 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6436 self.table.get_mut().get_mut(&Resource::from(id))
6437 }
6438
6439 pub fn add_child<T: 'static, U: 'static>(
6440 &mut self,
6441 child: TableId<T>,
6442 parent: TableId<U>,
6443 ) -> Result<(), ResourceTableError> {
6444 self.table
6445 .get_mut()
6446 .add_child(Resource::from(child), Resource::from(parent))
6447 }
6448
6449 pub fn remove_child<T: 'static, U: 'static>(
6450 &mut self,
6451 child: TableId<T>,
6452 parent: TableId<U>,
6453 ) -> Result<(), ResourceTableError> {
6454 self.table
6455 .get_mut()
6456 .remove_child(Resource::from(child), Resource::from(parent))
6457 }
6458
6459 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6460 self.table.get_mut().delete(Resource::from(id))
6461 }
6462
6463 fn push_future(&mut self, future: HostTaskFuture) {
6464 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6471 }
6472
6473 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6474 if self.switch_item.is_some() {
6475 self.push_high_priority(item);
6478 bail_bug!("switch item already set");
6479 }
6480
6481 self.switch_item = Some(item);
6482 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6483
6484 Ok(())
6485 }
6486
6487 fn take_next_switch_item(&mut self) -> Result<()> {
6488 if let Some(item) = self.next_switch_item.take() {
6489 self.set_switch_item(item)?;
6490 }
6491 Ok(())
6492 }
6493
6494 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6497 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6500 return Ok(());
6501 };
6502 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6503 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6504 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6505 }
6506 let instance = self.get_mut(thread.task)?.instance;
6507 self.get_mut(thread.thread)?.wake_on_cancel.take();
6508 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6509
6510 let item = match mode {
6511 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6512 instance,
6513 thread,
6514 fiber,
6515 },
6516 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6517 instance,
6518 call: GuestCall {
6519 thread,
6520 kind: GuestCallKind::DeliverEvent {
6521 instance: callback_instance,
6522 set: Some(set),
6523 },
6524 },
6525 },
6526 };
6527 self.push_high_priority(item);
6528 Ok(())
6529 }
6530
6531 fn push_high_priority(&mut self, item: WorkItem) {
6532 self.high_priority.push_front(item);
6533 log::trace!(
6534 "push high priority: {:?}",
6535 self.high_priority.front().unwrap()
6536 );
6537 }
6538
6539 fn push_low_priority(&mut self, item: WorkItem) {
6540 self.low_priority.push_front(item);
6541 log::trace!(
6542 "push low priority: {:?}",
6543 self.low_priority.front().unwrap()
6544 );
6545 }
6546
6547 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6548 match priority {
6549 Priority::Switch => self.set_switch_item(item)?,
6550 Priority::High => self.push_high_priority(item),
6551 Priority::Low => self.push_low_priority(item),
6552 }
6553
6554 Ok(())
6555 }
6556
6557 fn promote_instance_local_thread_work_item(
6558 &mut self,
6559 current_instance: RuntimeInstance,
6560 ) -> Result<bool> {
6561 log::trace!("promote thread work items for {current_instance:?}");
6562
6563 self.promote_work_item_matching(|item: &WorkItem| {
6564 let result = match item {
6565 WorkItem::ResumeThread { instance, .. }
6566 | WorkItem::ResumeFiber { instance, .. }
6567 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6568 _ => false,
6569 };
6570
6571 log::trace!("candidate {item:?}: {result}");
6572 result
6573 })
6574 }
6575
6576 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6577 self.promote_work_item_matching(|item: &WorkItem| match item {
6578 WorkItem::ResumeThread {
6579 thread: item_thread,
6580 ..
6581 }
6582 | WorkItem::GuestCall {
6583 call:
6584 GuestCall {
6585 thread: item_thread,
6586 ..
6587 },
6588 ..
6589 } => *item_thread == thread,
6590 _ => false,
6591 })
6592 }
6593
6594 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6595 where
6596 F: FnMut(&WorkItem) -> bool,
6597 {
6598 if self.switch_item.is_none() {
6605 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6606 self.switch_item = self.high_priority.remove(index);
6607 }
6608 }
6609 if self.switch_item.is_none() {
6610 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6611 self.switch_item = self.low_priority.remove(index);
6612 }
6613 }
6614 Ok(self.switch_item.is_some())
6615 }
6616
6617 pub fn call_context(&mut self, task: VMScope) -> Result<&mut CallContext> {
6620 match task.kind() {
6621 ScopeKind::HostId(task) => {
6622 let task: TableId<HostTask> = TableId::new(task);
6623 Ok(&mut self.get_mut(task)?.call_context)
6624 }
6625 ScopeKind::Id(task) => {
6626 let task: TableId<GuestTask> = TableId::new(task);
6627 Ok(&mut self.get_mut(task)?.call_context)
6628 }
6629 }
6630 }
6631
6632 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6633 self.deferred_host_call_context.as_mut()
6634 }
6635
6636 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6637 match self.futures.get_mut().as_mut() {
6638 Some(f) => Ok(f),
6639 None => bail_bug!("futures field of concurrent state is currently taken"),
6640 }
6641 }
6642
6643 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6644 self.table.get_mut()
6645 }
6646
6647 fn debug_assert_deferred_host_invariant(&self) {
6648 debug_assert_eq!(
6649 self.deferred_host_call_context.is_some(),
6650 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6651 "a deferred host thread and call context must exist together",
6652 );
6653 }
6654
6655 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6656 self.debug_assert_deferred_host_invariant();
6657 let caller = match self.unforced_current_thread {
6658 CurrentThread::DeferredHost(caller) => caller,
6659 thread => return Ok(thread),
6660 };
6661
6662 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6664 let task = self.push(task)?;
6665 let call_context = self
6666 .deferred_host_call_context
6667 .take()
6668 .expect("deferred host call context should be present");
6669 self.get_mut(task)
6670 .expect("newly inserted host task should be present")
6671 .call_context = call_context;
6672 self.unforced_current_thread = CurrentThread::Host(task);
6673 self.debug_assert_deferred_host_invariant();
6674 log::trace!("new host task materialized {task:?}");
6675 Ok(CurrentThread::Host(task))
6676 }
6677
6678 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6679 match self.materialize_host_task()? {
6680 CurrentThread::Host(id) => Ok(Some(id)),
6681 CurrentThread::None => Ok(None),
6682 CurrentThread::Guest(_) => {
6683 bail_bug!("tried to materialize a host task id from a guest thread")
6684 }
6685 CurrentThread::DeferredHost(_) => {
6686 bail_bug!(
6687 "current thread is a deferred host thread which should have been materialized"
6688 )
6689 }
6690 }
6691 }
6692
6693 pub(crate) fn materialize_current_scope(&mut self) -> Result<VMScope> {
6694 match self.materialize_host_task()? {
6695 CurrentThread::Host(id) => Ok(VMScope::host_id(id.rep())),
6696 _ => bail_bug!("current scope is not a deferred host scope"),
6697 }
6698 }
6699
6700 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6701 let count = &mut self.get_mut(task)?.ref_count;
6702 *count += 1;
6703 log::trace!("increment {task:?} to {count}");
6704 Ok(())
6705 }
6706
6707 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6708 let count = &mut self.get_mut(task)?.ref_count;
6709 if *count == 0 {
6710 bail_bug!("can't decrement task ref count that's already zero");
6711 }
6712 *count -= 1;
6713 log::trace!("decrement {task:?} to {count}");
6714 if *count == 0 {
6715 let task = self.delete(task)?;
6716
6717 self.decrement_group_ref_count(task.group)?;
6718
6719 debug_assert!(task.decremented_interesting_task_count);
6726 }
6727 Ok(())
6728 }
6729
6730 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6731 log::trace!("delete host task {task:?}");
6732 let task = self.delete(task)?;
6733
6734 self.decrement_group_ref_count(task.group)
6735 }
6736}
6737
6738fn for_any_lower<
6741 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6742>(
6743 fun: F,
6744) -> F {
6745 fun
6746}
6747
6748fn for_any_lift<
6750 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6751>(
6752 fun: F,
6753) -> F {
6754 fun
6755}
6756
6757fn check_ambient_store(id: StoreId) {
6758 let message = "\
6759 `Future`s which depend on asynchronous component tasks, streams, or \
6760 futures to complete may only be polled from the event loop of the \
6761 store to which they belong. Please use \
6762 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6763 ";
6764 tls::try_get(|store| {
6765 let matched = match store {
6766 tls::TryGet::Some(store) => store.id() == id,
6767 tls::TryGet::Taken | tls::TryGet::None => false,
6768 };
6769
6770 if !matched {
6771 panic!("{message}")
6772 }
6773 });
6774}
6775
6776fn unpack_callback_code(code: u32) -> (u32, u32) {
6777 (code & 0xF, code >> 4)
6778}
6779
6780struct WaitableCheckParams {
6784 set: TableId<WaitableSet>,
6785 options: OptionsIndex,
6786 payload: u32,
6787}
6788
6789enum WaitableCheck {
6792 Wait,
6793 Poll,
6794}
6795
6796pub(crate) struct PreparedCall<R> {
6798 handle: Func,
6800 thread: QualifiedThreadId,
6802 param_count: usize,
6804 rx: oneshot::Receiver<LiftedResult>,
6807 _phantom: PhantomData<R>,
6808}
6809
6810impl<R> PreparedCall<R> {
6811 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6818 store
6819 .concurrent_state_mut()?
6820 .increment_task_ref_count(self.thread.task)?;
6821
6822 Ok(TaskId {
6823 task: self.thread.task,
6824 })
6825 }
6826}
6827
6828pub(crate) struct TaskId {
6830 task: TableId<GuestTask>,
6831}
6832
6833impl TaskId {
6834 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6846 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6847 if !task.already_lowered_parameters() {
6848 task.lower_params = Some(Box::new(|store, _| {
6853 store.set_trapped();
6854 bail!("the host future for this call was dropped before it started")
6855 }));
6856 } else {
6857 _ = store
6862 .concurrent_state_mut()?
6863 .decrement_task_ref_count(self.task);
6864 }
6865
6866 Ok(())
6867 }
6868}
6869
6870pub(crate) fn prepare_call<T, R>(
6876 mut store: StoreContextMut<T>,
6877 handle: Func,
6878 param_count: usize,
6879 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6880 + Send
6881 + Sync
6882 + 'static,
6883 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6884 + Send
6885 + Sync
6886 + 'static,
6887) -> Result<PreparedCall<R>> {
6888 if !store.0.may_enter() {
6892 bail!(Trap::CannotEnterComponent);
6893 }
6894
6895 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6896
6897 let instance = handle.instance().id().get(store.0);
6898 let options = &instance.component().env_component().options[options];
6899 let ty = &instance.component().types()[ty];
6900 let async_typed = ty.async_;
6901 let async_lifted = raw_options.async_;
6902 let task_return_type = ty.results;
6903 let component_instance = raw_options.instance;
6904 let callback = options.callback.map(|i| instance.runtime_callback(i));
6905 let memory = options
6906 .memory()
6907 .map(|i| instance.runtime_memory(i))
6908 .map(SendSyncPtr::new);
6909 let string_encoding = options.string_encoding;
6910 let token = StoreToken::new(store.as_context_mut());
6911 let caller = store.0.materialize_host_task_id()?;
6912 let state = store.0.concurrent_state_mut()?;
6913
6914 let (tx, rx) = oneshot::channel();
6915
6916 let instance = handle.instance().runtime_instance(component_instance);
6917 let thread = GuestTask::new(
6918 state,
6919 Box::new(for_any_lower(move |store, params| {
6920 lower_params(token.as_context_mut(store), params)
6921 })),
6922 LiftResult {
6923 lift: Box::new(for_any_lift(move |store, result| {
6924 lift_result(store, result)
6925 })),
6926 ty: task_return_type,
6927 memory,
6928 string_encoding,
6929 },
6930 Caller::Host {
6931 tx: Some(tx),
6932 caller,
6933 },
6934 callback.map(|callback| {
6935 let callback = SendSyncPtr::new(callback);
6936 let instance = handle.instance();
6937 Box::new(move |store: &mut dyn VMStore, event, handle| {
6938 let store = token.as_context_mut(store);
6939 unsafe { instance.call_callback(store, callback, event, handle) }
6942 }) as CallbackFn
6943 }),
6944 instance,
6945 async_typed,
6946 async_lifted,
6947 )?;
6948
6949 Ok(PreparedCall {
6950 handle,
6951 thread,
6952 param_count,
6953 rx,
6954 _phantom: PhantomData,
6955 })
6956}
6957
6958pub(crate) struct StagedCall<R> {
6959 store: StoreId,
6960 rx: oneshot::Receiver<LiftedResult>,
6961 _marker: PhantomData<fn() -> R>,
6962 group: TaskGroupId,
6970}
6971
6972impl<R> StagedCall<R> {
6973 pub(crate) fn new<T: 'static>(
6984 mut store: StoreContextMut<T>,
6985 prepared: PreparedCall<R>,
6986 ) -> Result<StagedCall<R>> {
6987 let PreparedCall {
6988 handle,
6989 thread,
6990 param_count,
6991 rx,
6992 ..
6993 } = prepared;
6994
6995 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6996
6997 let state = store.0.concurrent_state_mut()?;
6998 let group = state.get_mut(thread.task)?.group;
6999 state.decrement_task_ref_count(thread.task)?;
7000
7001 Ok(StagedCall {
7002 store: store.0.id(),
7003 rx,
7004 _marker: PhantomData,
7005 group,
7006 })
7007 }
7008}
7009
7010impl<R> Future for StagedCall<R>
7011where
7012 R: 'static,
7013{
7014 type Output = Result<R>;
7015
7016 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
7017 check_ambient_store(self.store);
7018 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
7019 Ok(r) => match r.downcast() {
7020 Ok(r) => Ok(*r),
7021 Err(_) => bail_bug!("wrong type of value produced"),
7022 },
7023 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
7024 })
7025 }
7026}
7027
7028fn stage_call0<T: 'static>(
7031 store: StoreContextMut<T>,
7032 handle: Func,
7033 guest_thread: QualifiedThreadId,
7034 param_count: usize,
7035) -> Result<()> {
7036 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
7037 let is_concurrent = raw_options.async_;
7038 let callback = raw_options.callback;
7039 let instance = handle.instance();
7040 let callee = handle.lifted_core_func(store.0);
7041 let post_return = raw_options
7042 .post_return
7043 .map(|i| instance.id().get(store.0).runtime_post_return(i));
7044 let callback = callback.map(|i| {
7045 let instance = instance.id().get(store.0);
7046 SendSyncPtr::new(instance.runtime_callback(i))
7047 });
7048
7049 log::trace!("queueing call {guest_thread:?}");
7050
7051 unsafe {
7055 instance.stage_call(
7056 store,
7057 guest_thread,
7058 SendSyncPtr::new(callee),
7059 param_count,
7060 1,
7061 is_concurrent,
7062 callback,
7063 post_return.map(SendSyncPtr::new),
7064 true,
7065 )
7066 }
7067}
7068
7069#[cfg(all(test, feature = "cranelift", feature = "wat"))]
7070mod tests {
7071 use super::*;
7072 use crate::component::{Component, Linker};
7073 use crate::store::AsStoreOpaque;
7074 use crate::{Config, Engine};
7075
7076 fn host_subtask(
7077 state: HostTaskState,
7078 event: Option<Event>,
7079 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
7080 let mut config = Config::new();
7081 config.wasm_component_model_async(true);
7082 let engine = Engine::new(&config)?;
7083 let component = Component::new(&engine, "(component)")?;
7084 let mut store = Store::new(&engine, ());
7085 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
7086 let store_opaque = store.as_store_opaque();
7087 let concurrent_state = store_opaque.concurrent_state_mut()?;
7088 let group = concurrent_state.make_task_group()?;
7091 let task = concurrent_state.push(HostTask {
7092 common: WaitableCommon::default(),
7093 call_context: CallContext::default(),
7094 state,
7095 group,
7096 })?;
7097 let handle = store_opaque
7098 .handle_table_insertion(
7099 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
7100 )
7101 .subtask_host(task.rep())?;
7102 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7103 common.handle = Some(handle);
7104 common.event = event;
7105 Ok((store, instance, task, handle))
7106 }
7107
7108 #[test]
7109 fn host_subtask_drop_during_cancellation() -> Result<()> {
7110 for abort_completed in [false, true] {
7111 let (handle, future) = JoinHandle::run(future::pending::<()>());
7112 let mut future = pin!(future);
7113 let (mut store, instance, task, handle) =
7114 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7115 let store = store.as_store_opaque();
7116 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7117 assert_eq!(
7118 instance.subtask_cancel(store, caller, true, handle)?,
7119 BLOCKED
7120 );
7121 if abort_completed {
7122 assert!(matches!(
7125 future
7126 .as_mut()
7127 .poll(&mut Context::from_waker(Waker::noop())),
7128 Poll::Ready(None),
7129 ));
7130 }
7131 for async_ in [false, true] {
7132 let err = instance
7133 .subtask_cancel(store, caller, async_, handle)
7134 .unwrap_err();
7135 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7136 }
7137 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7138 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7139 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7140 }
7141 Ok(())
7142 }
7143
7144 #[test]
7145 fn host_subtask_cancel_after_completion() -> Result<()> {
7146 for async_ in [false, true] {
7147 let (mut store, instance, task, handle) = host_subtask(
7148 HostTaskState::CalleeDone { cancelled: false },
7149 Some(Event::Subtask {
7150 status: Status::Returned,
7151 }),
7152 )?;
7153 let store = store.as_store_opaque();
7154 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7155 assert_eq!(
7156 instance.subtask_cancel(store, caller, async_, handle)?,
7157 Status::Returned as u32,
7158 );
7159 let err = instance
7160 .subtask_cancel(store, caller, async_, handle)
7161 .unwrap_err();
7162 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7163 instance.subtask_drop(store, caller, handle)?;
7164 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7165 }
7166 Ok(())
7167 }
7168
7169 #[test]
7170 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7171 for (cancelled, status) in [
7172 (false, Status::Returned),
7173 (true, Status::Returned),
7174 (true, Status::ReturnCancelled),
7175 ] {
7176 for delivered in [false, true] {
7177 let event = if delivered {
7178 None
7179 } else {
7180 Some(Event::Subtask { status })
7181 };
7182 let (mut store, instance, task, handle) =
7183 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7184 let store = store.as_store_opaque();
7185 let result = instance.subtask_drop(
7186 store,
7187 RuntimeComponentInstanceIndex::from_u32(0),
7188 handle,
7189 );
7190 if delivered {
7191 result?;
7192 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7193 } else {
7194 let err = result.unwrap_err();
7195 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7196 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7197 }
7198 }
7199 }
7200 Ok(())
7201 }
7202}