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