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> {
794 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
795 let async_typed = task.async_typed;
796 let instance = task.instance;
797 let state = store.instance_state(instance).concurrent_state();
798
799 let ready = match &self.kind {
800 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
801 GuestCallKind::StartImplicit(_) => {
802 !async_typed || !(state.do_not_enter || state.backpressure > 0)
803 }
804 GuestCallKind::StartExplicit(_) => true,
805 };
806 log::trace!(
807 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
808 state.do_not_enter,
809 state.backpressure
810 );
811 Ok(ready)
812 }
813}
814
815enum WorkerItem {
817 GuestCall(GuestCall),
818 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
819}
820
821enum WorkItem {
824 PushFuture(AlwaysMut<HostTaskFuture>),
826 ResumeFiber {
828 instance: RuntimeInstance,
829 thread: QualifiedThreadId,
830 fiber: StoreFiber<'static>,
831 },
832 ResumeThread {
834 instance: RuntimeInstance,
835 thread: QualifiedThreadId,
836 },
837 GuestCall {
839 instance: RuntimeInstance,
840 call: GuestCall,
841 },
842 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
844}
845
846struct ResumingFiber<'a> {
852 store: &'a mut StoreOpaque,
853 fiber: Option<StoreFiber<'static>>,
854}
855
856impl Drop for ResumingFiber<'_> {
857 fn drop(&mut self) {
858 if let Some(mut fiber) = self.fiber.take() {
859 fiber.dispose(self.store);
860 }
861 }
862}
863
864impl fmt::Debug for WorkItem {
865 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
866 match self {
867 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
868 Self::ResumeFiber {
869 instance, thread, ..
870 } => f
871 .debug_struct("ResumeFiber")
872 .field("instance", instance)
873 .field("thread", thread)
874 .finish(),
875 Self::ResumeThread { instance, thread } => f
876 .debug_struct("ResumeThread")
877 .field("instance", instance)
878 .field("thread", thread)
879 .finish(),
880 Self::GuestCall { instance, call } => f
881 .debug_struct("GuestCall")
882 .field("instance", instance)
883 .field("call", call)
884 .finish(),
885 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
886 }
887 }
888}
889
890#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
892pub(crate) enum WaitResult {
893 Cancelled,
894 Completed,
895}
896
897pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
905 store: &mut dyn VMStore,
906 host_task: EnteredHostTask,
907 future: impl Future<Output = Result<R>> + Send + 'static,
908) -> Result<R> {
909 let mut future = Box::pin(future);
916 let poll = tls::set(store, || {
917 future
918 .as_mut()
919 .poll(&mut Context::from_waker(&Waker::noop()))
920 });
921
922 let caller = match host_task {
923 Some(caller) => caller,
924 None => bail_bug!("host task wasn't created but should have been"),
925 };
926
927 let task = match poll {
928 Poll::Ready(result) => return result,
930
931 Poll::Pending => {
936 let Some(task) = store.materialize_host_task_id()? else {
937 bail_bug!("current thread is not a host thread")
938 };
939
940 let future = Box::pin(async move {
943 let result = run_with_host_task_set(task, future).await??;
944 tls::get(move |store| {
945 let state = store.concurrent_state_mut()?;
946 let host_state = &mut state.get_mut(task)?.state;
947 assert!(matches!(host_state, HostTaskState::CalleeStarted));
948 *host_state = HostTaskState::CalleeFinished(Box::new(result));
949
950 Waitable::Host(task).set_event(
951 state,
952 Some(Event::Subtask {
953 status: Status::Returned,
954 }),
955 )?;
956
957 Ok(())
958 })
959 }) as HostTaskFuture;
960
961 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
962 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
963
964 let state = store.concurrent_state_mut()?;
965 state.push_future(future);
966
967 let set = state.get_mut(caller.thread)?.sync_call_set;
968 Waitable::Host(task).join(state, Some(set))?;
969
970 store.suspend(SuspendReason::Waiting {
971 set,
972 thread: caller,
973 })?;
974
975 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
979 task
980 }
981 };
982
983 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
985 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
986 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
987 Ok(result) => *result,
988 Err(_) => bail_bug!("host task finished with wrong type of result"),
989 }),
990 _ => bail_bug!("unexpected host task state after completion"),
991 }
992}
993
994fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
996 match call.kind {
997 GuestCallKind::DeliverEvent { instance, set } => {
998 if let Some(set) = set {
1001 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1002 }
1003 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1004 Some(pair) => pair,
1005 None => match set {
1006 Some(set) => {
1011 log::trace!(
1012 "event for {:?} on {set:?} no longer present; waiting again",
1013 call.thread
1014 );
1015 return instance.wait_with_callback(
1016 store.store_opaque_mut(),
1017 call.thread,
1018 set,
1019 );
1020 }
1021 None => (Event::None, None),
1026 },
1027 };
1028 let state = store.concurrent_state_mut()?;
1029 if let Some(set) = set
1034 && waitable.is_none()
1035 && !state.get_mut(set)?.ready.is_empty()
1036 {
1037 state.wake_waiter(set)?;
1038 }
1039 let task = state.get_mut(call.thread.task)?;
1040 let runtime_instance = task.instance;
1041 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1042
1043 log::trace!(
1044 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1045 call.thread,
1046 );
1047
1048 instance.handle_callback_code(store, call.thread, runtime_instance, |store| {
1049 invoke_callback(store, call.thread.task, event, handle)
1050 })?;
1051 }
1052 GuestCallKind::StartImplicit(fun) => {
1053 fun(store)?;
1054 }
1055 GuestCallKind::StartExplicit(fun) => {
1056 fun(store)?;
1057 }
1058 }
1059
1060 Ok(())
1061}
1062
1063fn invoke_callback(
1066 store: &mut dyn VMStore,
1067 task: TableId<GuestTask>,
1068 event: Event,
1069 handle: u32,
1070) -> Result<u32> {
1071 let Some(callback) = store.concurrent_state_mut()?.get_mut(task)?.callback.take() else {
1072 bail_bug!("guest task callback field not present")
1073 };
1074
1075 let code = callback(store, event, handle)?;
1076
1077 store.concurrent_state_mut()?.get_mut(task)?.callback = Some(callback);
1078
1079 Ok(code)
1080}
1081
1082impl<T> Store<T> {
1083 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1085 where
1086 T: Send + 'static,
1087 {
1088 ensure!(
1089 self.as_context().0.concurrency_support(),
1090 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1091 );
1092 self.as_context_mut().run_concurrent(fun).await
1093 }
1094
1095 #[doc(hidden)]
1096 pub fn assert_concurrent_state_empty(&mut self) {
1097 self.as_context_mut().assert_concurrent_state_empty();
1098 }
1099
1100 #[doc(hidden)]
1101 pub fn concurrent_state_table_size(&mut self) -> usize {
1102 self.as_context_mut().concurrent_state_table_size()
1103 }
1104
1105 pub fn spawn(
1107 &mut self,
1108 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1109 ) -> Result<JoinHandle>
1110 where
1111 T: 'static,
1112 {
1113 self.as_context_mut().spawn(task)
1114 }
1115}
1116
1117impl<T> StoreContextMut<'_, T> {
1118 #[doc(hidden)]
1129 pub fn assert_concurrent_state_empty(self) {
1130 let store = self.0;
1131 store
1132 .store_data_mut()
1133 .components
1134 .assert_instance_states_empty();
1135 let state = store.concurrent_state_mut().unwrap();
1136 assert!(
1137 state.table.get_mut().is_empty(),
1138 "non-empty table: {:?}",
1139 state.table.get_mut()
1140 );
1141 assert!(state.switch_item.is_none());
1142 assert!(state.next_switch_item.is_none());
1143 assert!(state.high_priority.is_empty());
1144 assert!(state.low_priority.is_empty());
1145 assert!(state.saved_next_switch_items.is_empty());
1146 assert!(state.unforced_current_thread.is_none());
1147 assert!(state.deferred_host_call_context.is_none());
1148 assert!(state.futures_mut().unwrap().is_empty());
1149 assert!(state.global_error_context_ref_counts.is_empty());
1150 }
1151
1152 #[doc(hidden)]
1157 pub fn concurrent_state_table_size(&mut self) -> usize {
1158 self.0.concurrent_state_mut().unwrap().table.get_mut().len()
1159 }
1160
1161 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1171 where
1172 T: 'static,
1173 {
1174 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1175 self.spawn_with_accessor(accessor, task)
1176 }
1177
1178 fn spawn_with_accessor<D>(
1181 self,
1182 accessor: Accessor<T, D>,
1183 task: impl for<'fut> AccessorTask<'fut, T, D>,
1184 ) -> Result<JoinHandle>
1185 where
1186 T: 'static,
1187 D: HasData + ?Sized,
1188 {
1189 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1193 self.0
1194 .concurrent_state_mut()?
1195 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1196 Ok(handle)
1197 }
1198
1199 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1283 where
1284 T: Send + 'static,
1285 {
1286 ensure!(
1287 self.0.concurrency_support(),
1288 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1289 );
1290 self.do_run_concurrent(fun, false).await
1291 }
1292
1293 pub(super) async fn run_concurrent_trap_on_idle<R>(
1294 self,
1295 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1296 ) -> Result<R> {
1297 self.do_run_concurrent(fun, true).await
1298 }
1299
1300 async fn do_run_concurrent<R>(
1301 mut self,
1302 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1303 trap_on_idle: bool,
1304 ) -> Result<R> {
1305 debug_assert!(self.0.concurrency_support());
1306 self.0.check_event_loop_not_running()?;
1307 let token = StoreToken::new(self.as_context_mut());
1308
1309 struct Dropper<'a, T: 'static, V> {
1310 store: StoreContextMut<'a, T>,
1311 value: ManuallyDrop<V>,
1312 }
1313
1314 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1315 fn drop(&mut self) {
1316 self.store
1317 .0
1318 .concurrent_state_mut_already_forced_current_thread()
1319 .event_loop_running = false;
1320
1321 tls::set(self.store.0, || {
1322 unsafe { ManuallyDrop::drop(&mut self.value) }
1327 });
1328 }
1329 }
1330
1331 let accessor = &Accessor::new(token);
1332 self.0
1333 .concurrent_state_mut_already_forced_current_thread()
1334 .event_loop_running = true;
1335 let dropper = &mut Dropper {
1336 store: self,
1337 value: ManuallyDrop::new(fun(accessor)),
1338 };
1339 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1341
1342 let result = dropper
1343 .store
1344 .as_context_mut()
1345 .poll_until(future, trap_on_idle)
1346 .await;
1347
1348 if result.is_err() {
1349 dropper.store.0.set_trapped();
1350 }
1351
1352 result
1353 }
1354
1355 async fn poll_until<R>(
1361 mut self,
1362 mut future: Pin<&mut impl Future<Output = R>>,
1363 trap_on_idle: bool,
1364 ) -> Result<R> {
1365 struct Reset<'a, T: 'static> {
1366 store: StoreContextMut<'a, T>,
1367 futures: Option<FuturesUnordered<HostTaskFuture>>,
1368 }
1369
1370 impl<'a, T> Drop for Reset<'a, T> {
1371 fn drop(&mut self) {
1372 if let Some(futures) = self.futures.take() {
1373 *self
1374 .store
1375 .0
1376 .concurrent_state_mut_already_forced_current_thread()
1377 .futures
1378 .get_mut() = Some(futures);
1379 }
1380 }
1381 }
1382
1383 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1387 let mut turns_without_yield = 0;
1388
1389 loop {
1390 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1394 let mut reset = Reset {
1395 store: self.as_context_mut(),
1396 futures,
1397 };
1398 let mut next = match reset.futures.as_mut() {
1399 Some(f) => pin!(f.next()),
1400 None => bail_bug!("concurrent state missing futures field"),
1401 };
1402
1403 enum PollResult<R> {
1404 Complete(R),
1405 ProcessWork {
1406 ready: Option<WorkItem>,
1407 low_priority: bool,
1408 },
1409 }
1410
1411 let result = future::poll_fn(|cx| {
1412 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1415 return Poll::Ready(Ok(PollResult::Complete(value)));
1416 }
1417
1418 if reset.store.0.trapped() {
1427 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1428 }
1429
1430 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1434 Poll::Ready(Some(output)) => {
1435 match output {
1436 Err(e) => return Poll::Ready(Err(e)),
1437 Ok(()) => {}
1438 }
1439 Poll::Ready(true)
1440 }
1441 Poll::Ready(None) => Poll::Ready(false),
1442 Poll::Pending => Poll::Pending,
1443 };
1444
1445 let state = reset.store.0.concurrent_state_mut()?;
1460 let mut ready = state.switch_item.take();
1461 let mut low_priority = false;
1462 if ready.is_none() {
1463 ready = state.high_priority.pop_back();
1464 if ready.is_none() {
1465 ready = state.low_priority.pop_back();
1466 low_priority = true;
1467 }
1468 }
1469 if ready.is_some() {
1470 return Poll::Ready(Ok(PollResult::ProcessWork {
1471 ready,
1472 low_priority,
1473 }));
1474 }
1475
1476 return match next {
1480 Poll::Ready(true) => {
1481 Poll::Ready(Ok(PollResult::ProcessWork {
1487 ready: None,
1488 low_priority: false,
1489 }))
1490 }
1491 Poll::Ready(false) => {
1492 if let Poll::Ready(value) =
1496 tls::set(reset.store.0, || future.as_mut().poll(cx))
1497 {
1498 Poll::Ready(Ok(PollResult::Complete(value)))
1499 } else {
1500 if trap_on_idle {
1506 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1513 Trap::CannotBlockSyncTask.into()
1514 } else {
1515 Trap::AsyncDeadlock.into()
1517 }))
1518 } else {
1519 Poll::Pending
1523 }
1524 }
1525 }
1526 Poll::Pending => Poll::Pending,
1531 };
1532 })
1533 .await;
1534
1535 drop(reset);
1539
1540 match result? {
1541 PollResult::Complete(value) => break Ok(value),
1544 PollResult::ProcessWork {
1547 mut ready,
1548 low_priority,
1549 } => {
1550 if low_priority {
1578 if let Some(item) = ready.take() {
1579 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1580 }
1581 self.0.yield_now().await;
1582 turns_without_yield = 0;
1583 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1584 }
1585
1586 if let Some(item) = ready {
1587 self.as_context_mut().handle_work_item(item).await?;
1588 }
1589
1590 turns_without_yield += 1;
1591 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1592 turns_without_yield = 0;
1593 self.0.yield_now().await;
1594 }
1595 }
1596 }
1597 }
1598 }
1599
1600 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1602 let item = match item {
1606 WorkItem::ResumeFiber {
1607 instance,
1608 thread,
1609 fiber,
1610 } => {
1611 let future = self.0.resume_fiber(fiber);
1612 log::trace!(
1613 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1614 );
1615 return futures::future::Either::Left(future);
1616 }
1617 item => item,
1618 };
1619 futures::future::Either::Right(async move {
1620 log::trace!("handle work item {item:?}");
1621 match item {
1622 WorkItem::PushFuture(future) => {
1623 self.0
1624 .concurrent_state_mut()?
1625 .futures_mut()?
1626 .push(future.into_inner());
1627 }
1628 WorkItem::ResumeFiber { .. } => unreachable!(),
1629 WorkItem::ResumeThread { thread, .. } => {
1630 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1631 if !matches!(slot, GuestThreadState::Ready { .. }) {
1632 bail_bug!("cannot resume non-pending thread {thread:?}");
1633 }
1634 let GuestThreadState::Ready { fiber } =
1635 mem::replace(slot, GuestThreadState::Running)
1636 else {
1637 unreachable!()
1638 };
1639 self.0.resume_fiber(fiber).await?;
1640 }
1641 WorkItem::GuestCall { call, .. } => {
1642 if call.is_ready(self.0)? {
1643 self.0
1644 .concurrent_state_mut()?
1645 .get_mut(call.thread.thread)?
1646 .wake_on_cancel = WakeOnCancel::None;
1647 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1648 } else {
1649 let state = self.0.concurrent_state_mut()?;
1650 let task = state.get_mut(call.thread.task)?;
1651 if !task.starting_sent {
1652 task.starting_sent = true;
1653 if let GuestCallKind::StartImplicit(_) = &call.kind {
1654 Waitable::Guest(call.thread.task).set_event(
1655 state,
1656 Some(Event::Subtask {
1657 status: Status::Starting,
1658 }),
1659 )?;
1660 }
1661 }
1662
1663 let instance = state.get_mut(call.thread.task)?.instance;
1664 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1673 let state = self.0.concurrent_state_mut()?;
1674 if !state.get_mut(*set)?.ready.is_empty() {
1675 state.wake_waiter(*set)?;
1676 }
1677 }
1678 self.0
1679 .instance_state(instance)
1680 .concurrent_state()
1681 .pending
1682 .insert(call.thread, call.kind);
1683
1684 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1688 }
1689 }
1690 WorkItem::WorkerFunction(fun) => {
1691 self.run_on_worker(WorkerItem::Function(fun)).await?;
1692 }
1693 }
1694
1695 Ok(())
1696 })
1697 }
1698
1699 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1701 let state = self.0.concurrent_state_mut()?;
1702 if state.worker_item.is_some() {
1703 bail_bug!("worker item already set when taking worker fiber");
1704 }
1705 let worker = if let Some(fiber) = state.worker.take() {
1706 fiber
1707 } else {
1708 unsafe {
1727 fiber::make_fiber_unchecked(self.0, move |store| {
1728 loop {
1729 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1730 bail_bug!("worker_item not present when resuming fiber")
1731 };
1732 match item {
1733 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1734 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1735 }
1736
1737 store.suspend(SuspendReason::NeedWork)?;
1738 }
1739 })?
1740 }
1741 };
1742
1743 self.0
1746 .concurrent_state_mut_already_forced_current_thread()
1747 .worker_item = Some(item);
1748
1749 self.0.resume_fiber(worker).await
1750 }
1751
1752 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1757 where
1758 T: 'static,
1759 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1760 + Send
1761 + Sync
1762 + 'static,
1763 R: Send + Sync + 'static,
1764 {
1765 let token = StoreToken::new(self);
1766 async move {
1767 let mut accessor = Accessor::new(token);
1768 closure(&mut accessor).await
1769 }
1770 }
1771
1772 pub(crate) async fn start_instance(
1773 &mut self,
1774 instance: ModuleInstance,
1775 callee: Option<RuntimeInstance>,
1776 ) -> Result<ModuleInstance> {
1777 let (tx, rx) = oneshot::channel();
1778 let token = StoreToken::new(self.as_context_mut());
1779 self.0.queue_task(move |store| {
1780 _ = tx.send(
1781 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1782 .map(|()| instance),
1783 );
1784 Ok(())
1785 })?;
1786 self.as_context_mut()
1787 .run_concurrent_trap_on_idle(async |_| {
1788 rx.await
1789 .map_err(|_| format_err!("oneshot channel canceled"))
1790 })
1791 .await??
1792 }
1793}
1794
1795pub type EnteredHostTask = Option<QualifiedThreadId>;
1802
1803impl StoreOpaque {
1804 pub(crate) fn check_event_loop_not_running(&mut self) -> Result<()> {
1805 if self
1806 .concurrent_state_mut_already_forced_current_thread()
1807 .event_loop_running
1808 {
1809 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1810 }
1811 Ok(())
1812 }
1813
1814 #[inline]
1818 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1819 if !self.concurrency_support() {
1821 return Ok(CurrentThread::None);
1822 }
1823
1824 if !self
1827 .vm_store_context_mut()
1828 .current_thread_mut()
1829 .is_deferred()
1830 {
1831 return Ok(self
1832 .concurrent_state_mut_already_forced_current_thread()
1833 .unforced_current_thread);
1834 }
1835
1836 self.force_deferred_current_thread()
1837 }
1838
1839 #[cold]
1842 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1843 let state = self.concurrent_state_mut_without_forcing_current_thread();
1852 let id = match state.unforced_current_thread.guest_task() {
1853 Some(task) => state.get_mut(task)?.instance.instance,
1854 None => bail_bug!("deferred component-model thread with non-guest base"),
1855 };
1856
1857 let mut frames = Vec::new();
1860 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1861 while let Some(ptr) = cur.as_deferred() {
1862 let deferred = unsafe { ptr.as_non_null().as_ref() };
1867 frames.push((
1868 deferred.callee_async != 0,
1869 deferred.callee_instance,
1870 deferred.saved_context,
1871 ));
1872 cur = deferred.parent;
1873 }
1874
1875 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1879
1880 let current_context = *self.vm_store_context_mut().component_context_mut();
1883
1884 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1888 *self.vm_store_context_mut().component_context_mut() = saved_context;
1892 let callee = RuntimeInstance {
1893 instance: id,
1894 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1895 };
1896 self.enter_guest_sync_call(callee_async, callee)?;
1897 }
1898
1899 *self.vm_store_context_mut().component_context_mut() = current_context;
1901
1902 Ok(self
1903 .concurrent_state_mut_without_forcing_current_thread()
1904 .unforced_current_thread)
1905 }
1906
1907 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1908 match self.current_thread()?.guest() {
1909 Some(id) => Ok(*id),
1910 None => bail_bug!("current thread is not a guest thread"),
1911 }
1912 }
1913
1914 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1918 match self.current_thread()? {
1919 CurrentThread::Host(id) => Ok(Some(id)),
1920 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1921 _ => bail_bug!("current thread is not a host thread"),
1922 }
1923 }
1924
1925 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1928 Ok(self
1929 .concurrent_state_mut()?
1930 .materialize_current_host_task_id()?)
1931 }
1932
1933 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1934 log::trace!("enter sync-typed call {callee:?}");
1935 let state = self.instance_state(callee).concurrent_state();
1936 let old_do_not_suspend = state.do_not_suspend;
1937 state.do_not_suspend = true;
1938
1939 let thread = self.current_guest_thread()?;
1940 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1941 if thread.old_do_not_suspend.is_some() {
1942 bail_bug!("current thread already has `old_do_not_suspend` value");
1943 }
1944
1945 thread.old_do_not_suspend = Some(old_do_not_suspend);
1946
1947 Ok(())
1948 }
1949
1950 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1951 log::trace!("exit sync-typed call {callee:?}");
1952 let thread = self.current_guest_thread()?;
1953 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1954 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1955 bail_bug!("current thread missing `old_do_not_suspend` value");
1956 };
1957 let state = self.instance_state(callee).concurrent_state();
1958 state.do_not_suspend = old_do_not_suspend;
1959 Ok(())
1960 }
1961
1962 pub(crate) fn enter_guest_sync_call(
1974 &mut self,
1975 callee_async_typed: bool,
1976 callee: RuntimeInstance,
1977 ) -> Result<()> {
1978 log::trace!("enter sync-lifted call {callee:?}");
1979 if !self.concurrency_support() {
1980 return self.enter_call_not_concurrent();
1981 }
1982
1983 let state = self.concurrent_state_mut()?;
1987 let item = state.next_switch_item.take();
1988 state.saved_next_switch_items.push(item);
1989
1990 let thread = self.current_thread()?;
1991 let caller = if let Some(thread) = thread.guest() {
1992 Caller::Guest { thread: *thread }
1993 } else {
1994 Caller::Host {
1995 tx: None,
1996 caller: self.materialize_host_task_id()?,
1997 }
1998 };
1999
2000 let state = self.concurrent_state_mut()?;
2001 let guest_thread = GuestTask::new(
2002 state,
2003 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2004 LiftResult {
2005 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2006 ty: TypeTupleIndex::reserved_value(),
2007 memory: None,
2008 string_encoding: StringEncoding::Utf8,
2009 },
2010 caller,
2011 None,
2012 callee,
2013 callee_async_typed,
2014 false,
2015 )?;
2016
2017 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2018 guest_thread.thread,
2019 self,
2020 callee.index,
2021 )?;
2022 self.set_thread(guest_thread)?;
2023
2024 if !callee_async_typed {
2025 self.enter_sync_call(callee)?;
2026 }
2027
2028 Ok(())
2029 }
2030
2031 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2039 if !self.concurrency_support() {
2040 return Ok(self.exit_call_not_concurrent());
2041 }
2042
2043 let thread = match self.current_thread()?.guest() {
2044 Some(t) => *t,
2045 None => bail_bug!("expected task when exiting"),
2046 };
2047 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2048 let instance = task.instance;
2049
2050 let caller = match &task.caller {
2051 &Caller::Guest { thread } => thread.into(),
2052 &Caller::Host { caller, .. } => caller
2053 .map(CurrentThread::Host)
2054 .unwrap_or(CurrentThread::None),
2055 };
2056 task.lift_result = None;
2057 task.exited = true;
2058 let async_typed = task.async_typed;
2059
2060 if !async_typed {
2061 self.exit_sync_call(instance)?;
2062 }
2063
2064 self.set_thread(caller)?;
2065
2066 log::trace!("exit sync-lifted call {instance:?}");
2067
2068 if async_typed {
2069 self.switch_or_trap_if_may_not_suspend(instance)?;
2074 }
2075
2076 self.cleanup_thread(thread, instance)?;
2077
2078 self.concurrent_state_mut()?
2079 .decrement_task_ref_count(thread.task)?;
2080
2081 let state = self.concurrent_state_mut()?;
2082 let Some(item) = state.saved_next_switch_items.pop() else {
2083 bail_bug!("unable to pop from `saved_next_switch_items`");
2084 };
2085 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2086 state.push_high_priority(item);
2089 bail_bug!("`next_switch_item` unexpectedly already set");
2090 }
2091
2092 Ok(())
2093 }
2094
2095 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2102 if !self.concurrency_support() {
2103 self.enter_call_not_concurrent()?;
2104 return Ok(None);
2105 }
2106 let caller = self.current_guest_thread()?;
2107 log::trace!("new deferred host task with caller {caller:?}");
2108
2109 self.set_thread(CurrentThread::DeferredHost(caller))?;
2110 let state = self.concurrent_state_mut()?;
2111 debug_assert!(state.deferred_host_call_context.is_none());
2112 state.deferred_host_call_context = Some(CallContext::default());
2113 state.debug_assert_deferred_host_invariant();
2114 Ok(Some(caller))
2115 }
2116
2117 pub(crate) fn host_task_delete(
2124 &mut self,
2125 original_task: EnteredHostTask,
2126 materialized_task: Option<TableId<HostTask>>,
2127 ) -> Result<()> {
2128 match original_task {
2129 Some(caller) => {
2130 self.set_thread(caller)?;
2131 if materialized_task.is_none() {
2132 let state = self.concurrent_state_mut()?;
2133 let context = state
2134 .deferred_host_call_context
2135 .take()
2136 .expect("deferred host call context should be present");
2137 debug_assert!(context.is_empty());
2138 state.debug_assert_deferred_host_invariant();
2139 }
2140 log::trace!(
2141 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2142 );
2143 if let Some(task) = materialized_task {
2144 self.concurrent_state_mut()?.delete_host_task(task)?;
2145 }
2146 }
2147 None => {
2148 debug_assert!(materialized_task.is_none());
2149 self.exit_call_not_concurrent();
2150 }
2151 }
2152 Ok(())
2153 }
2154
2155 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2158 self.component_instance_mut(instance.instance)
2159 .instance_state(instance.index)
2160 }
2161
2162 fn instance_state_and_handle_counter(
2165 &mut self,
2166 instance: RuntimeInstance,
2167 ) -> (&mut InstanceState, &mut HandleCounter) {
2168 let (component, counter) = self
2169 .store_data_mut()
2170 .component_instance_and_handle_counter_mut(instance.instance);
2171 (component.instance_state(instance.index), counter)
2172 }
2173
2174 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2180 let thread = thread.into();
2181 let state = self.concurrent_state_mut()?;
2182 state.debug_assert_deferred_host_invariant();
2183 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2184
2185 state.handle_thread_switch(old_thread, thread)?;
2186
2187 if let Some(old_thread) = old_thread.guest() {
2195 let old_context = *self.vm_store_context_mut().component_context_mut();
2196 self.concurrent_state_mut()?
2197 .get_mut(old_thread.thread)?
2198 .context = old_context;
2199 }
2200 if cfg!(debug_assertions) {
2201 *self.vm_store_context_mut().component_context_mut() =
2202 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2203 }
2204 if let Some(thread) = thread.guest() {
2205 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2206 let context = thread.context;
2207 if cfg!(debug_assertions) {
2208 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2209 }
2210 *self.vm_store_context_mut().component_context_mut() = context;
2211 }
2212
2213 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2215 VMLazyThread::none()
2216 } else {
2217 VMLazyThread::forced()
2218 };
2219
2220 Ok(old_thread)
2221 }
2222
2223 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2225 if self.switch_if_may_not_suspend(instance)? {
2226 Ok(())
2227 } else {
2228 Err(Trap::CannotBlockSyncTask.into())
2229 }
2230 }
2231
2232 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2236 self.concurrent_state_mut()?;
2240
2241 Ok(!self.concurrency_support()
2242 || !self
2243 .instance_state(instance)
2244 .concurrent_state()
2245 .do_not_suspend
2246 || self
2247 .concurrent_state_mut()?
2248 .promote_instance_local_thread_work_item(instance)?)
2249 }
2250
2251 fn enter_instance(&mut self, instance: RuntimeInstance) {
2255 log::trace!("enter {instance:?}");
2256 self.instance_state(instance)
2257 .concurrent_state()
2258 .do_not_enter = true;
2259 }
2260
2261 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2265 log::trace!("exit {instance:?}");
2266 self.instance_state(instance)
2267 .concurrent_state()
2268 .do_not_enter = false;
2269 self.partition_pending(instance)
2270 }
2271
2272 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2280 for (thread, kind) in
2281 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2282 {
2283 let call = GuestCall { thread, kind };
2284 if call.is_ready(self)? {
2285 self.concurrent_state_mut()?
2286 .push_high_priority(WorkItem::GuestCall { instance, call });
2287 } else {
2288 self.instance_state(instance)
2289 .concurrent_state()
2290 .pending
2291 .insert(call.thread, call.kind);
2292 }
2293 }
2294
2295 if let Some(waker) = self
2296 .concurrent_state_mut()?
2297 .ready_for_concurrent_call_waker
2298 .take()
2299 {
2300 waker.wake();
2301 }
2302
2303 Ok(())
2304 }
2305
2306 pub(crate) fn backpressure_modify(
2308 &mut self,
2309 caller_instance: RuntimeInstance,
2310 modify: impl FnOnce(u16) -> Option<u16>,
2311 ) -> Result<()> {
2312 let state = self.instance_state(caller_instance).concurrent_state();
2313 let old = state.backpressure;
2314 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2315 state.backpressure = new;
2316
2317 if old > 0 && new == 0 {
2318 self.partition_pending(caller_instance)?;
2321 }
2322
2323 Ok(())
2324 }
2325
2326 fn resume_fiber(
2329 &mut self,
2330 fiber: StoreFiber<'static>,
2331 ) -> impl Future<Output = Result<()>> + '_ {
2332 let mut resuming = ResumingFiber {
2333 store: self,
2334 fiber: Some(fiber),
2335 };
2336 async move {
2337 let old_thread = resuming.store.current_thread()?;
2338 log::trace!("resume_fiber: save current thread {old_thread:?}");
2339
2340 let fiber = resuming.fiber.take().unwrap();
2341 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2342
2343 resuming.store.set_thread(old_thread)?;
2344
2345 let state = resuming.store.concurrent_state_mut()?;
2346
2347 if let Some(ot) = old_thread.guest() {
2348 state.set_thread_running(ot.thread)?;
2349 }
2350 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2351
2352 if resuming.fiber.is_some() {
2353 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2354 let reason = match state.suspend_reason.take() {
2356 Some(r) => r,
2357 None => bail_bug!("suspend reason missing when resuming fiber"),
2358 };
2359 match reason {
2360 SuspendReason::NeedWork => {
2361 if state.worker.is_none() {
2362 state.worker = resuming.fiber.take();
2363 }
2364 }
2365 SuspendReason::Yielding { thread } => {
2366 let slot = &mut state.get_mut(thread.thread)?.state;
2367 slot.check_no_fiber()?;
2368 *slot = GuestThreadState::Ready {
2369 fiber: resuming.fiber.take().unwrap(),
2370 };
2371 let instance = state.get_mut(thread.task)?.instance;
2372 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2373 }
2374 SuspendReason::ExplicitlySuspending { thread } => {
2375 let slot = &mut state.get_mut(thread.thread)?.state;
2376 slot.check_no_fiber()?;
2377 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2378 }
2379 SuspendReason::Waiting { set, thread } => {
2380 match state.get_mut(set)?.waiting.entry(thread) {
2381 Entry::Vacant(entry) => {
2382 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2383 }
2384 Entry::Occupied(_) => {
2385 bail_bug!("entry unexpectedly already exists for {thread:?}");
2386 }
2387 }
2388 }
2389 SuspendReason::YieldingToSubtask { thread } => {
2390 let instance = state.get_mut(thread.task)?.instance;
2399 if state.next_switch_item.is_some() {
2400 bail_bug!(
2403 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2404 a thread wanted to wait on a subtask"
2405 );
2406 }
2407 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2408 instance,
2409 thread,
2410 fiber: resuming.fiber.take().unwrap(),
2411 });
2412 log::trace!("set next switch item to {item:?}");
2413 }
2414 };
2415 } else {
2416 log::trace!("resume_fiber: fiber has exited");
2417 }
2418
2419 Ok(())
2420 }
2421 }
2422
2423 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2429 log::trace!("suspend fiber: {reason:?}");
2430
2431 if !self.fiber_async_state_mut().can_block() {
2434 return Err(format_err!("future dropped"));
2435 }
2436
2437 let state = self.concurrent_state_mut()?;
2438
2439 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2446 SuspendReason::Yielding { .. }
2447 | SuspendReason::Waiting { .. }
2448 | SuspendReason::ExplicitlySuspending { .. } => {
2449 if state.switch_item.is_none() {
2452 state.take_next_switch_item()?;
2453 }
2454
2455 (true, false)
2456 }
2457 SuspendReason::YieldingToSubtask { .. } => (true, true),
2458 SuspendReason::NeedWork => (false, false),
2459 };
2460
2461 let old_next_switch_item = if save_and_restore_next_switch_item {
2462 Some(state.save_next_switch_item()?)
2463 } else {
2464 None
2465 };
2466
2467 let old_guest_thread = if save_and_restore_thread {
2468 self.current_thread()?
2469 } else {
2470 CurrentThread::None
2471 };
2472
2473 let waiting_set = match &reason {
2474 SuspendReason::Waiting { set, .. } => Some(*set),
2475 _ => None,
2476 };
2477
2478 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2479 if suspend_reason.is_some() {
2480 bail_bug!("suspend reason unexpectedly already set for store");
2481 }
2482 *suspend_reason = Some(reason);
2483
2484 if let Some(set) = waiting_set {
2487 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2488 }
2489
2490 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2491
2492 if let Some(set) = waiting_set {
2493 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2494 }
2495
2496 if save_and_restore_thread {
2497 self.set_thread(old_guest_thread)?;
2498 }
2499
2500 if let Some(item) = old_next_switch_item {
2501 let state = self.concurrent_state_mut()?;
2502 state.restore_next_switch_item(item)?;
2503 }
2504
2505 Ok(())
2506 }
2507
2508 fn wait_for_event(
2509 &mut self,
2510 caller_instance: RuntimeInstance,
2511 waitable: Waitable,
2512 ) -> Result<()> {
2513 let caller = self.current_guest_thread()?;
2514 let state = self.concurrent_state_mut()?;
2515
2516 waitable.trap_if_in_waitable_set(state)?;
2517
2518 let set = state.get_mut(caller.thread)?.sync_call_set;
2519 waitable.join(state, Some(set))?;
2520
2521 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2522
2523 self.suspend(SuspendReason::Waiting {
2524 set,
2525 thread: caller,
2526 })?;
2527 let state = self.concurrent_state_mut()?;
2528
2529 waitable.join(state, None)
2530 }
2531
2532 fn cleanup_thread(
2554 &mut self,
2555 guest_thread: QualifiedThreadId,
2556 runtime_instance: RuntimeInstance,
2557 ) -> Result<()> {
2558 let state = self.concurrent_state_mut()?;
2559 state.take_next_switch_item()?;
2562 let thread_data = state.get_mut(guest_thread.thread)?;
2563 let sync_call_set = thread_data.sync_call_set;
2564 if let Some(guest_id) = thread_data.instance_rep {
2565 let (state, counter) = self.instance_state_and_handle_counter(runtime_instance);
2566 state
2567 .thread_handle_table()
2568 .guest_thread_remove(counter, guest_id)?;
2569 }
2570 let state = self.concurrent_state_mut()?;
2571
2572 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2574 if let Some(Event::Subtask {
2575 status: Status::Returned | Status::ReturnCancelled,
2576 }) = waitable.common(state)?.event
2577 {
2578 waitable.delete_from(state)?;
2579 }
2580 }
2581
2582 state.delete(guest_thread.thread)?;
2583 state.delete(sync_call_set)?;
2584 let task = state.get_mut(guest_thread.task)?;
2585 task.threads.remove(&guest_thread.thread);
2586
2587 if task.threads.is_empty() && !task.returned_or_cancelled() {
2588 bail!(Trap::NoAsyncResult);
2589 }
2590
2591 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2592 task.decremented_interesting_task_count = true;
2593
2594 debug_assert!(state.interesting_tasks > 0);
2595 state.interesting_tasks -= 1;
2596 if state.interesting_tasks == 0
2597 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2598 {
2599 waker.wake();
2600 }
2601 }
2602
2603 state.decrement_task_ref_count(guest_thread.task)?;
2604
2605 Ok(())
2606 }
2607
2608 fn cancel_guest_subtask_without_lowered_parameters(
2617 &mut self,
2618 caller_instance: RuntimeInstance,
2619 guest_task: TableId<GuestTask>,
2620 ) -> Result<()> {
2621 let concurrent_state = self.concurrent_state_mut()?;
2622 let task = concurrent_state.get_mut(guest_task)?;
2623 assert!(!task.already_lowered_parameters());
2624 task.lower_params = None;
2628 task.lift_result = None;
2629 task.exited = true;
2630 let instance = task.instance;
2631
2632 assert_eq!(1, task.threads.len());
2635 let thread = *task.threads.iter().next().unwrap();
2636 self.cleanup_thread(
2637 QualifiedThreadId {
2638 task: guest_task,
2639 thread,
2640 },
2641 caller_instance,
2642 )?;
2643
2644 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2646 let pending_count = pending.len();
2647 pending.retain(|thread, _| thread.task != guest_task);
2648
2649 if pending.len() == pending_count {
2650 let matches = |item: &WorkItem| match item {
2653 WorkItem::ResumeThread { thread, .. }
2654 | WorkItem::GuestCall {
2655 call: GuestCall { thread, .. },
2656 ..
2657 } => thread.task == guest_task,
2658 _ => false,
2659 };
2660
2661 let remove = |queue: &mut VecDeque<WorkItem>| {
2662 let count = queue.len();
2663 queue.retain(|item| !matches(item));
2664 count != queue.len()
2665 };
2666
2667 let state = self.concurrent_state_mut()?;
2668 let mut found = remove(&mut state.high_priority);
2669 found |= remove(&mut state.low_priority);
2670
2671 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2672 if matches!(item, Some(item) if matches(item)) {
2673 found = true;
2674 *item = None;
2675 }
2676 }
2677
2678 if !found {
2679 bail!(Trap::SubtaskCancelAfterTerminal);
2680 }
2681 }
2682 Ok(())
2683 }
2684
2685 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2688 if !self.concurrency_support() {
2689 return Ok(self
2690 .current_scope_id_not_concurrent()?
2691 .map(|id| CurrentScope::Id(Scope::Id(id))));
2692 }
2693
2694 Ok(match self.current_thread()? {
2695 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2696 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2697 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2698 CurrentThread::None => return Ok(None),
2699 })
2700 }
2701
2702 pub(crate) fn queue_task(
2703 &mut self,
2704 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2705 ) -> Result<()> {
2706 self.concurrent_state_mut()?
2707 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2708 Ok(())
2709 }
2710
2711 fn any_may_not_suspend(&mut self) -> Result<bool> {
2720 Ok(self
2728 .concurrent_state_mut()?
2729 .table
2730 .get_mut()
2731 .iter_mut()
2732 .filter_map(|(_, entry)| {
2733 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2734 Some(task.instance)
2735 } else {
2736 None
2737 }
2738 })
2739 .collect::<Vec<_>>()
2740 .into_iter()
2741 .any(|instance| {
2742 self.instance_state(instance)
2743 .concurrent_state()
2744 .do_not_suspend
2745 }))
2746 }
2747}
2748
2749impl Instance {
2750 fn get_event(
2753 self,
2754 store: &mut StoreOpaque,
2755 guest_task: TableId<GuestTask>,
2756 set: Option<TableId<WaitableSet>>,
2757 cancellable: bool,
2758 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2759 let state = store.concurrent_state_mut()?;
2760
2761 let task = state.get_mut(guest_task)?;
2762 let event = &mut task.event;
2763 if let Some(ev) = event
2764 && (cancellable || !matches!(ev, Event::Cancelled))
2765 {
2766 log::trace!("deliver event {ev:?} to {guest_task:?}");
2767
2768 if matches!(ev, Event::Cancelled) {
2769 task.cancel_request_delivered = true;
2770 }
2771
2772 let ev = *ev;
2773 *event = None;
2774 return Ok(Some((ev, None)));
2775 }
2776
2777 let set = match set {
2778 Some(set) => set,
2779 None => return Ok(None),
2780 };
2781 let waitable = match state.get_mut(set)?.ready.pop_first() {
2782 Some(v) => v,
2783 None => return Ok(None),
2784 };
2785
2786 let common = waitable.common(state)?;
2787 let handle = match common.handle {
2788 Some(h) => h,
2789 None => bail_bug!("handle not set when delivering event"),
2790 };
2791 let event = match common.event.take() {
2792 Some(e) => e,
2793 None => bail_bug!("event not set when delivering event"),
2794 };
2795
2796 log::trace!(
2797 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2798 );
2799
2800 waitable.on_delivery(store, self, event)?;
2801
2802 Ok(Some((event, Some((waitable, handle)))))
2803 }
2804
2805 fn handle_callback_code(
2812 self,
2813 store: &mut dyn VMStore,
2814 guest_thread: QualifiedThreadId,
2815 runtime_instance: RuntimeInstance,
2816 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2817 ) -> Result<()> {
2818 let old_thread = store.set_thread(guest_thread)?;
2819 log::trace!(
2820 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2821 );
2822
2823 store.enter_instance(runtime_instance);
2824
2825 let mut code = call(store)?;
2826
2827 if matches!(
2833 unpack_callback_code(code).0,
2834 callback_code::YIELD | callback_code::WAIT
2835 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2836 {
2837 let (unpacked, set) = unpack_callback_code(code);
2838 log::trace!(
2839 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2840 cancellation; delivering it via callback"
2841 );
2842 code = invoke_callback(store, guest_thread.task, event, 0)?;
2843 }
2844
2845 store.exit_instance(runtime_instance)?;
2846
2847 store.set_thread(old_thread)?;
2848 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2849
2850 let store = store.store_opaque_mut();
2851 let (code, set) = unpack_callback_code(code);
2852
2853 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2854
2855 let state = store.concurrent_state_mut()?;
2856
2857 state.take_next_switch_item()?;
2858
2859 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2860 let set = store
2861 .instance_state(runtime_instance)
2862 .handle_table()
2863 .waitable_set_rep(handle)?;
2864
2865 Ok(TableId::<WaitableSet>::new(set))
2866 };
2867
2868 match code {
2869 callback_code::EXIT => {
2870 log::trace!("implicit thread {guest_thread:?} completed");
2871 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2872 task.exited = true;
2873 task.callback = None;
2874
2875 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2880
2881 store.cleanup_thread(guest_thread, runtime_instance)?;
2882 }
2883 callback_code::YIELD => {
2884 let old = state
2887 .get_mut(guest_thread.thread)?
2888 .wake_on_cancel
2889 .replace(WakeOnCancel::Yielding);
2890 if !old.is_none() {
2891 bail_bug!("thread unexpectedly had wake_on_cancel set");
2892 }
2893
2894 let call = GuestCall {
2901 thread: guest_thread,
2902 kind: GuestCallKind::DeliverEvent {
2903 instance: self,
2904 set: None,
2905 },
2906 };
2907 state.push_low_priority(WorkItem::GuestCall {
2910 instance: runtime_instance,
2911 call,
2912 });
2913 }
2914 callback_code::WAIT => {
2915 let set = get_set(store, set)?;
2916 self.wait_with_callback(store, guest_thread, set)?;
2917 }
2918 _ => bail!(Trap::UnsupportedCallbackCode),
2919 }
2920
2921 Ok(())
2922 }
2923
2924 fn wait_with_callback(
2930 self,
2931 store: &mut StoreOpaque,
2932 guest_thread: QualifiedThreadId,
2933 set: TableId<WaitableSet>,
2934 ) -> Result<()> {
2935 let state = store.concurrent_state_mut()?;
2936 state.get_mut(set)?.num_waiting += 1;
2939
2940 if state.get_mut(guest_thread.task)?.event.is_some()
2941 || !state.get_mut(set)?.ready.is_empty()
2942 {
2943 let instance = state.get_mut(guest_thread.task)?.instance;
2945 state.push_high_priority(WorkItem::GuestCall {
2946 instance,
2947 call: GuestCall {
2948 thread: guest_thread,
2949 kind: GuestCallKind::DeliverEvent {
2950 instance: self,
2951 set: Some(set),
2952 },
2953 },
2954 });
2955 return Ok(());
2956 }
2957
2958 let instance = store
2964 .concurrent_state_mut()?
2965 .get_mut(guest_thread.task)?
2966 .instance;
2967 store.switch_or_trap_if_may_not_suspend(instance)?;
2968
2969 let state = store.concurrent_state_mut()?;
2975 let old = state
2976 .get_mut(guest_thread.thread)?
2977 .wake_on_cancel
2978 .replace(WakeOnCancel::Waiting(set));
2979 if !old.is_none() {
2980 bail_bug!("thread unexpectedly had wake_on_cancel set");
2981 }
2982 let old = state
2983 .get_mut(set)?
2984 .waiting
2985 .insert(guest_thread, WaitMode::Callback(self));
2986 if !old.is_none() {
2987 bail_bug!("set's waiting set already had this thread registered");
2988 }
2989 Ok(())
2990 }
2991
2992 unsafe fn stage_call<T: 'static>(
2999 self,
3000 mut store: StoreContextMut<T>,
3001 guest_thread: QualifiedThreadId,
3002 callee: SendSyncPtr<VMFuncRef>,
3003 param_count: usize,
3004 result_count: usize,
3005 async_: bool,
3006 callback: Option<SendSyncPtr<VMFuncRef>>,
3007 post_return: Option<SendSyncPtr<VMFuncRef>>,
3008 host_caller: bool,
3009 ) -> Result<()> {
3010 unsafe fn make_call<T: 'static>(
3025 store: StoreContextMut<T>,
3026 guest_thread: QualifiedThreadId,
3027 callee: SendSyncPtr<VMFuncRef>,
3028 param_count: usize,
3029 result_count: usize,
3030 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3031 + Send
3032 + Sync
3033 + 'static
3034 + use<T> {
3035 let token = StoreToken::new(store);
3036 move |store: &mut dyn VMStore| {
3037 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3038
3039 store
3040 .concurrent_state_mut()?
3041 .set_thread_running(guest_thread.thread)?;
3042 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3043 let lower = match task.lower_params.take() {
3044 Some(l) => l,
3045 None => bail_bug!("lower_params missing"),
3046 };
3047
3048 lower(store, &mut storage[..param_count])?;
3049
3050 let mut store = token.as_context_mut(store);
3051
3052 unsafe {
3055 crate::Func::call_unchecked_raw(
3056 &mut store,
3057 callee.as_non_null(),
3058 NonNull::new(
3059 &mut storage[..param_count.max(result_count)]
3060 as *mut [MaybeUninit<ValRaw>] as _,
3061 )
3062 .unwrap(),
3063 UncaughtException::Trap,
3064 )?;
3065 }
3066
3067 Ok(storage)
3068 }
3069 }
3070
3071 let call = unsafe {
3075 make_call(
3076 store.as_context_mut(),
3077 guest_thread,
3078 callee,
3079 param_count,
3080 result_count,
3081 )
3082 };
3083
3084 let callee_instance = store
3085 .0
3086 .concurrent_state_mut()?
3087 .get_mut(guest_thread.task)?
3088 .instance;
3089
3090 let fun = if callback.is_some() {
3091 assert!(async_);
3092
3093 Box::new(move |store: &mut dyn VMStore| {
3094 self.add_guest_thread_to_instance_table(
3095 guest_thread.thread,
3096 store,
3097 callee_instance.index,
3098 )?;
3099 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3100 let storage = call(store)?;
3101
3102 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3105 })
3106 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3107 } else {
3108 let token = StoreToken::new(store.as_context_mut());
3109 Box::new(move |store: &mut dyn VMStore| {
3110 self.add_guest_thread_to_instance_table(
3111 guest_thread.thread,
3112 store,
3113 callee_instance.index,
3114 )?;
3115 let old_thread = store.set_thread(guest_thread)?;
3116 log::trace!(
3117 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3118 );
3119 let flags = self.id().get(store).instance_flags(callee_instance.index);
3120
3121 let callee_async_typed = store
3122 .concurrent_state_mut()?
3123 .get_mut(guest_thread.task)?
3124 .async_typed;
3125
3126 if !async_ && callee_async_typed {
3130 store.enter_instance(callee_instance);
3131 }
3132
3133 if !callee_async_typed {
3134 store.enter_sync_call(callee_instance)?;
3135 }
3136
3137 let storage = call(store)?;
3144
3145 if !callee_async_typed {
3146 store.exit_sync_call(callee_instance)?;
3147 }
3148
3149 if !async_ {
3150 if callee_async_typed {
3156 store.exit_instance(callee_instance)?;
3157 }
3158
3159 let lift = {
3160 let state = store.concurrent_state_mut()?;
3161 if !state.get_mut(guest_thread.task)?.result.is_none() {
3162 bail_bug!("task has already produced a result");
3163 }
3164
3165 match state.get_mut(guest_thread.task)?.lift_result.take() {
3166 Some(lift) => lift,
3167 None => bail_bug!("lift_result field is missing"),
3168 }
3169 };
3170
3171 let result = (lift.lift)(store, unsafe {
3174 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3175 &storage[..result_count],
3176 )
3177 })?;
3178
3179 let post_return_arg = match result_count {
3180 0 => ValRaw::i32(0),
3181 1 => unsafe { storage[0].assume_init() },
3184 _ => unreachable!(),
3185 };
3186
3187 unsafe {
3188 call_post_return(
3189 token.as_context_mut(store),
3190 post_return.map(|v| v.as_non_null()),
3191 post_return_arg,
3192 flags,
3193 )?;
3194 }
3195
3196 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3197 }
3198
3199 store.set_thread(old_thread)?;
3200
3201 store
3202 .concurrent_state_mut()?
3203 .get_mut(guest_thread.task)?
3204 .exited = true;
3205
3206 log::trace!(
3207 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3208 );
3209
3210 if callee_async_typed {
3211 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3216 }
3217
3218 store.cleanup_thread(guest_thread, callee_instance)?;
3220 Ok(())
3221 })
3222 };
3223
3224 store.0.concurrent_state_mut()?.push_work_item(
3225 WorkItem::GuestCall {
3226 instance: callee_instance,
3227 call: GuestCall {
3228 thread: guest_thread,
3229 kind: GuestCallKind::StartImplicit(fun),
3230 },
3231 },
3232 if host_caller {
3233 Priority::High
3234 } else {
3235 Priority::Switch
3236 },
3237 )?;
3238
3239 Ok(())
3240 }
3241
3242 unsafe fn prepare_call<T: 'static>(
3255 self,
3256 mut store: StoreContextMut<T>,
3257 start: NonNull<VMFuncRef>,
3258 return_: NonNull<VMFuncRef>,
3259 caller_instance: RuntimeComponentInstanceIndex,
3260 callee_instance: RuntimeComponentInstanceIndex,
3261 task_return_type: TypeTupleIndex,
3262 callee_async_typed: bool,
3263 memory: *mut VMMemoryDefinition,
3264 string_encoding: StringEncoding,
3265 caller_info: CallerInfo,
3266 ) -> Result<()> {
3267 enum ResultInfo {
3268 Heap { results: u32 },
3269 Stack { result_count: u32 },
3270 }
3271
3272 let result_info = match &caller_info {
3273 CallerInfo::Async {
3274 has_result: true,
3275 params,
3276 } => ResultInfo::Heap {
3277 results: match params.last() {
3279 Some(r) => r.get_u32(),
3280 None => bail_bug!("retptr missing"),
3281 },
3282 },
3283 CallerInfo::Async {
3284 has_result: false, ..
3285 } => ResultInfo::Stack { result_count: 0 },
3286 CallerInfo::Sync {
3287 result_count,
3288 params,
3289 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3290 results: match params.last() {
3292 Some(r) => r.get_u32(),
3293 None => bail_bug!("arg ptr missing"),
3294 },
3295 },
3296 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3297 result_count: *result_count,
3298 },
3299 };
3300
3301 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3302
3303 let start = SendSyncPtr::new(start);
3307 let return_ = SendSyncPtr::new(return_);
3308 let token = StoreToken::new(store.as_context_mut());
3309 let old_thread = store.0.current_guest_thread()?;
3310
3311 let state = store.0.concurrent_state_mut()?;
3312
3313 debug_assert_eq!(
3314 state.get_mut(old_thread.task)?.instance,
3315 self.runtime_instance(caller_instance)
3316 );
3317
3318 let guest_thread = GuestTask::new(
3319 state,
3320 Box::new(move |store, dst| {
3321 let mut store = token.as_context_mut(store);
3322 assert!(dst.len() <= MAX_FLAT_PARAMS);
3323 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3325 let count = match caller_info {
3326 CallerInfo::Async { params, has_result } => {
3330 let params = ¶ms[..params.len() - usize::from(has_result)];
3331 for (param, src) in params.iter().zip(&mut src) {
3332 src.write(*param);
3333 }
3334 params.len()
3335 }
3336
3337 CallerInfo::Sync { params, .. } => {
3339 for (param, src) in params.iter().zip(&mut src) {
3340 src.write(*param);
3341 }
3342 params.len()
3343 }
3344 };
3345 unsafe {
3352 crate::Func::call_unchecked_raw(
3353 &mut store,
3354 start.as_non_null(),
3355 NonNull::new(
3356 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3357 )
3358 .unwrap(),
3359 UncaughtException::Trap,
3360 )?;
3361 }
3362 dst.copy_from_slice(&src[..dst.len()]);
3363 let task = store.0.current_guest_thread()?.task;
3364 let state = store.0.concurrent_state_mut()?;
3365 Waitable::Guest(task).set_event(
3366 state,
3367 Some(Event::Subtask {
3368 status: Status::Started,
3369 }),
3370 )?;
3371 Ok(())
3372 }),
3373 LiftResult {
3374 lift: Box::new(move |store, src| {
3375 let mut store = token.as_context_mut(store);
3378 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3380 my_src.push(ValRaw::u32(*results));
3381 }
3382
3383 unsafe {
3390 crate::Func::call_unchecked_raw(
3391 &mut store,
3392 return_.as_non_null(),
3393 my_src.as_mut_slice().into(),
3394 UncaughtException::Trap,
3395 )?;
3396 }
3397
3398 let thread = store.0.current_guest_thread()?;
3399 let state = store.0.concurrent_state_mut()?;
3400 if sync_caller {
3401 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3402 if let ResultInfo::Stack { result_count } = &result_info {
3403 match result_count {
3404 0 => None,
3405 1 => Some(my_src[0]),
3406 _ => unreachable!(),
3407 }
3408 } else {
3409 None
3410 },
3411 );
3412 }
3413 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3414 }),
3415 ty: task_return_type,
3416 memory: NonNull::new(memory).map(SendSyncPtr::new),
3417 string_encoding,
3418 },
3419 Caller::Guest { thread: old_thread },
3420 None,
3421 self.runtime_instance(callee_instance),
3422 callee_async_typed,
3423 false,
3426 )?;
3427
3428 store.0.set_thread(guest_thread)?;
3431 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3432
3433 Ok(())
3434 }
3435
3436 unsafe fn call_callback<T>(
3441 self,
3442 mut store: StoreContextMut<T>,
3443 function: SendSyncPtr<VMFuncRef>,
3444 event: Event,
3445 handle: u32,
3446 ) -> Result<u32> {
3447 let (ordinal, result) = event.parts();
3448 let params = &mut [
3449 ValRaw::u32(ordinal),
3450 ValRaw::u32(handle),
3451 ValRaw::u32(result),
3452 ];
3453 unsafe {
3458 crate::Func::call_unchecked_raw(
3459 &mut store,
3460 function.as_non_null(),
3461 params.as_mut_slice().into(),
3462 UncaughtException::Trap,
3463 )?;
3464 }
3465 Ok(params[0].get_u32())
3466 }
3467
3468 unsafe fn start_call<T: 'static>(
3486 self,
3487 mut store: StoreContextMut<T>,
3488 callback: *mut VMFuncRef,
3489 post_return: *mut VMFuncRef,
3490 callee: NonNull<VMFuncRef>,
3491 param_count: u32,
3492 result_count: u32,
3493 flags: u32,
3494 storage: &mut [MaybeUninit<ValRaw>],
3495 ) -> Result<()> {
3496 let token = StoreToken::new(store.as_context_mut());
3497 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3498 let guest_thread = store.0.current_guest_thread()?;
3499 let state = store.0.concurrent_state_mut()?;
3500
3501 if !state.event_loop_running {
3502 bail_bug!("Instance::start_call called without a running event loop");
3503 }
3504
3505 let callee = SendSyncPtr::new(callee);
3506 let param_count = usize::try_from(param_count)?;
3507 assert!(param_count <= MAX_FLAT_PARAMS);
3508 let result_count = usize::try_from(result_count)?;
3509 assert!(result_count <= MAX_FLAT_RESULTS);
3510
3511 let task = state.get_mut(guest_thread.task)?;
3512 let callee_async_typed = task.async_typed;
3513 let callee_instance = task.instance;
3514
3515 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3516
3517 if let Some(callback) = NonNull::new(callback) {
3518 let callback = SendSyncPtr::new(callback);
3522 task.callback = Some(Box::new(move |store, event, handle| {
3523 let store = token.as_context_mut(store);
3524 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3525 }));
3526 }
3527
3528 let Caller::Guest { thread: caller } = &task.caller else {
3529 bail_bug!("start_call unexpectedly invoked for host->guest call");
3532 };
3533 let caller = *caller;
3534 let caller_instance = state.get_mut(caller.task)?.instance;
3535
3536 unsafe {
3538 self.stage_call(
3539 store.as_context_mut(),
3540 guest_thread,
3541 callee,
3542 param_count,
3543 result_count,
3544 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3545 NonNull::new(callback).map(SendSyncPtr::new),
3546 NonNull::new(post_return).map(SendSyncPtr::new),
3547 false,
3548 )?;
3549 }
3550
3551 let old_do_not_suspend = if callee_async_typed {
3552 let state = store.0.instance_state(callee_instance).concurrent_state();
3559 let old_do_not_suspend = state.do_not_suspend;
3560 state.do_not_suspend = false;
3561 Some(old_do_not_suspend)
3562 } else {
3563 None
3564 };
3565
3566 let state = store.0.concurrent_state_mut()?;
3567
3568 let guest_waitable = Waitable::Guest(guest_thread.task);
3571 let old_set = guest_waitable.common(state)?.set;
3572 let set = state.get_mut(caller.thread)?.sync_call_set;
3573 guest_waitable.join(state, Some(set))?;
3574
3575 store.0.set_thread(CurrentThread::None)?;
3576
3577 let mut yielded = false;
3593 let (status, waitable) = loop {
3594 store.0.suspend(if yielded {
3595 SuspendReason::Waiting {
3596 set,
3597 thread: caller,
3598 }
3599 } else {
3600 yielded = true;
3601 SuspendReason::YieldingToSubtask { thread: caller }
3602 })?;
3603
3604 if let Some(old_do_not_suspend) = old_do_not_suspend {
3605 store
3606 .0
3607 .instance_state(callee_instance)
3608 .concurrent_state()
3609 .do_not_suspend = old_do_not_suspend;
3610 }
3611
3612 let state = store.0.concurrent_state_mut()?;
3613
3614 log::trace!("taking event for {:?}", guest_thread.task);
3615 let event = guest_waitable.take_event(state)?;
3616 let Some(Event::Subtask { status }) = event else {
3617 bail_bug!("subtasks should only get subtask events, got {event:?}")
3618 };
3619
3620 log::trace!("status {status:?} for {:?}", guest_thread.task);
3621
3622 if status == Status::Returned {
3623 break (status, None);
3625 } else if async_caller {
3626 let (state, counter) = store.0.instance_state_and_handle_counter(caller_instance);
3630 let handle = state
3631 .handle_table()
3632 .subtask_insert_guest(counter, guest_thread.task.rep())?;
3633 store
3634 .0
3635 .concurrent_state_mut()?
3636 .get_mut(guest_thread.task)?
3637 .common
3638 .handle = Some(handle);
3639 break (status, Some(handle));
3640 } else {
3641 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3645 }
3646 };
3647
3648 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3649
3650 store.0.set_thread(caller)?;
3652
3653 store
3654 .0
3655 .concurrent_state_mut()?
3656 .set_thread_running(caller.thread)?;
3657
3658 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3659
3660 if async_caller {
3661 let Some(slot) = storage.first_mut() else {
3662 bail_bug!("no storage for async call status");
3663 };
3664 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3665 } else {
3666 let state = store.0.concurrent_state_mut()?;
3669 let task = state.get_mut(guest_thread.task)?;
3670 if let Some(Some(result)) = task.sync_result.take()? {
3671 storage[0] = MaybeUninit::new(result);
3672 }
3673 }
3674
3675 if status == Status::Returned {
3676 store
3677 .0
3678 .concurrent_state_mut()?
3679 .decrement_task_ref_count(guest_thread.task)?;
3680 }
3681
3682 Ok(())
3683 }
3684
3685 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3701 self,
3702 mut store: StoreContextMut<'_, T>,
3703 host_task: EnteredHostTask,
3704 result_may_require_realloc: bool,
3705 future: impl Future<Output = Result<R>> + Send + 'static,
3706 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3707 + Send
3708 + 'static,
3709 ) -> Result<u32> {
3710 let token = StoreToken::new(store.as_context_mut());
3711
3712 let (join_handle, future) = JoinHandle::run(future);
3715 let mut future = Box::pin(future);
3716
3717 let poll = tls::set(store.0, || {
3722 future
3723 .as_mut()
3724 .poll(&mut Context::from_waker(&Waker::noop()))
3725 });
3726
3727 match poll {
3728 Poll::Ready(result) => {
3730 let result = result.transpose()?;
3731 let task = store.0.current_materialized_host_task()?;
3734 lower(store.as_context_mut(), result, true, task)?;
3735 return Ok(Status::Returned.pack(None));
3736 }
3737
3738 Poll::Pending => {}
3740 }
3741
3742 let Some(task) = store.0.materialize_host_task_id()? else {
3746 bail_bug!("current thread is not a host thread")
3747 };
3748 {
3749 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3750 assert!(matches!(state, HostTaskState::CalleeStarted));
3751 *state = HostTaskState::CalleeRunning(join_handle);
3752 }
3753
3754 let future = Box::pin(async move {
3762 let result = match run_with_host_task_set(task, future).await? {
3763 Some(result) => Some(result?),
3764 None => None,
3765 };
3766 let on_complete = move |store: &mut dyn VMStore| {
3767 let mut store = token.as_context_mut(store);
3771 let old = store.0.set_thread(task)?;
3772
3773 let status = if result.is_some() {
3774 Status::Returned
3775 } else {
3776 Status::ReturnCancelled
3777 };
3778
3779 lower(store.as_context_mut(), result, false, Some(task))?;
3780 let state = store.0.concurrent_state_mut()?;
3781 match &mut state.get_mut(task)?.state {
3782 pending @ HostTaskState::CalleeCancelling => {
3785 *pending = HostTaskState::CalleeDone { cancelled: true };
3786 }
3787
3788 other => *other = HostTaskState::CalleeDone { cancelled: false },
3790 }
3791 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3792
3793 store.0.set_thread(old)?;
3794 Ok(())
3795 };
3796
3797 tls::get(move |store| {
3798 if result_may_require_realloc {
3799 store
3804 .concurrent_state_mut()?
3805 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3806 on_complete,
3807 ))));
3808 Ok(())
3809 } else {
3810 on_complete(store)
3813 }
3814 })
3815 });
3816
3817 let caller = match host_task {
3820 Some(caller) => caller,
3821 None => bail_bug!("host task wasn't created but should have been"),
3822 };
3823 let state = store.0.concurrent_state_mut()?;
3824 state.push_future(future);
3825 let instance = state.get_mut(caller.task)?.instance;
3826 let (state, counter) = store.0.instance_state_and_handle_counter(instance);
3827 let handle = state
3828 .handle_table()
3829 .subtask_insert_host(counter, task.rep())?;
3830 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3831 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3832
3833 store.0.set_thread(caller)?;
3837 Ok(Status::Started.pack(Some(handle)))
3838 }
3839
3840 pub(crate) fn task_return(
3843 self,
3844 store: &mut dyn VMStore,
3845 ty: TypeTupleIndex,
3846 options: OptionsIndex,
3847 storage: &[ValRaw],
3848 ) -> Result<()> {
3849 let guest_thread = store.current_guest_thread()?;
3850 let state = store.concurrent_state_mut()?;
3851 if !state.get_mut(guest_thread.task)?.async_lifted {
3852 bail!(Trap::TaskReturnOrCancelSyncLifted);
3853 }
3854 let lift = state
3855 .get_mut(guest_thread.task)?
3856 .lift_result
3857 .take()
3858 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3859 if !state.get_mut(guest_thread.task)?.result.is_none() {
3860 bail_bug!("task result unexpectedly already set");
3861 }
3862
3863 let CanonicalOptions {
3864 string_encoding,
3865 data_model,
3866 ..
3867 } = &self.id().get(store).component().env_component().options[options];
3868
3869 let invalid = ty != lift.ty
3870 || string_encoding != &lift.string_encoding
3871 || match data_model {
3872 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3873 Some(memory) => {
3874 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3875 let actual = self.id().get(store).runtime_memory(memory);
3876 expected != actual.as_ptr()
3877 }
3878 None => false,
3881 },
3882 CanonicalOptionsDataModel::Gc { .. } => true,
3884 };
3885
3886 if invalid {
3887 bail!(Trap::TaskReturnInvalid);
3888 }
3889
3890 log::trace!("task.return for {guest_thread:?}");
3891
3892 let result = (lift.lift)(store, storage)?;
3893 self.task_complete(store, guest_thread.task, result, Status::Returned)
3894 }
3895
3896 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3898 let guest_thread = store.current_guest_thread()?;
3899 let state = store.concurrent_state_mut()?;
3900 let task = state.get_mut(guest_thread.task)?;
3901 if !task.async_lifted {
3902 bail!(Trap::TaskReturnOrCancelSyncLifted);
3903 }
3904 if !task.cancel_request_delivered {
3905 bail!(Trap::TaskCancelNotCancelled);
3906 }
3907 _ = task
3908 .lift_result
3909 .take()
3910 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3911
3912 if !task.result.is_none() {
3913 bail_bug!("task result should not bet set yet");
3914 }
3915
3916 log::trace!("task.cancel for {guest_thread:?}");
3917
3918 self.task_complete(
3919 store,
3920 guest_thread.task,
3921 Box::new(DummyResult),
3922 Status::ReturnCancelled,
3923 )
3924 }
3925
3926 fn task_complete(
3932 self,
3933 store: &mut StoreOpaque,
3934 guest_task: TableId<GuestTask>,
3935 result: Box<dyn Any + Send + Sync>,
3936 status: Status,
3937 ) -> Result<()> {
3938 store
3939 .component_resource_tables(Some(self))?
3940 .validate_scope_exit()?;
3941
3942 let state = store.concurrent_state_mut()?;
3943 let task = state.get_mut(guest_task)?;
3944
3945 task.event = None;
3949
3950 if let Caller::Host { tx, .. } = &mut task.caller {
3951 if let Some(tx) = tx.take() {
3952 _ = tx.send(result);
3953 }
3954 } else {
3955 task.result = Some(result);
3956 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3957 }
3958
3959 Ok(())
3960 }
3961
3962 pub(crate) fn waitable_set_new(
3964 self,
3965 store: &mut StoreOpaque,
3966 caller_instance: RuntimeComponentInstanceIndex,
3967 ) -> Result<u32> {
3968 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3969 let (state, counter) =
3970 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3971 let handle = state
3972 .handle_table()
3973 .waitable_set_insert(counter, set.rep())?;
3974 log::trace!("new waitable set {set:?} (handle {handle})");
3975 Ok(handle)
3976 }
3977
3978 pub(crate) fn waitable_set_drop(
3980 self,
3981 store: &mut StoreOpaque,
3982 caller_instance: RuntimeComponentInstanceIndex,
3983 set: u32,
3984 ) -> Result<()> {
3985 let (state, counter) =
3986 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3987 let rep = state.handle_table().waitable_set_remove(counter, set)?;
3988
3989 log::trace!("drop waitable set {rep} (handle {set})");
3990
3991 let set = store
3995 .concurrent_state_mut()?
3996 .get_mut(TableId::<WaitableSet>::new(rep))?;
3997 if set.num_waiting > 0 {
3998 bail!(Trap::WaitableSetDropHasWaiters);
3999 }
4000
4001 store
4002 .concurrent_state_mut()?
4003 .delete(TableId::<WaitableSet>::new(rep))?;
4004
4005 Ok(())
4006 }
4007
4008 pub(crate) fn waitable_join(
4010 self,
4011 store: &mut StoreOpaque,
4012 caller_instance: RuntimeComponentInstanceIndex,
4013 waitable_handle: u32,
4014 set_handle: u32,
4015 ) -> Result<()> {
4016 let mut instance = self.id().get_mut(store);
4017 let waitable =
4018 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4019
4020 let set = if set_handle == 0 {
4021 None
4022 } else {
4023 let set = instance.instance_states().0[caller_instance]
4024 .handle_table()
4025 .waitable_set_rep(set_handle)?;
4026
4027 Some(TableId::<WaitableSet>::new(set))
4028 };
4029
4030 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4031
4032 log::trace!(
4033 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4034 );
4035
4036 waitable.join(store.concurrent_state_mut()?, set)
4037 }
4038
4039 pub(crate) fn subtask_drop(
4041 self,
4042 store: &mut StoreOpaque,
4043 caller_instance: RuntimeComponentInstanceIndex,
4044 task_id: u32,
4045 ) -> Result<()> {
4046 self.waitable_join(store, caller_instance, task_id, 0)?;
4047
4048 let (state, counter) =
4049 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
4050 let (rep, is_host) = state.handle_table().subtask_remove(counter, task_id)?;
4051
4052 let concurrent_state = store.concurrent_state_mut()?;
4053 let waitable = if is_host {
4054 let id = TableId::<HostTask>::new(rep);
4055 let task = concurrent_state.get_mut(id)?;
4056 match &task.state {
4057 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4058 bail!(Trap::SubtaskDropNotResolved)
4059 }
4060 HostTaskState::CalleeDone { .. } => {}
4061 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4062 bail_bug!("invalid state for callee in `subtask.drop`")
4063 }
4064 }
4065
4066 Waitable::Host(id)
4067 } else {
4068 let id = TableId::<GuestTask>::new(rep);
4069 let task = concurrent_state.get_mut(id)?;
4070 if task.lift_result.is_some() {
4071 bail!(Trap::SubtaskDropNotResolved);
4072 }
4073 Waitable::Guest(id)
4074 };
4075
4076 waitable.common(concurrent_state)?.handle = None;
4077
4078 if waitable.take_event(concurrent_state)?.is_some() {
4081 bail!(Trap::SubtaskDropNotResolved);
4082 }
4083
4084 waitable.delete_from(concurrent_state)?;
4085
4086 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4087 Ok(())
4088 }
4089
4090 pub(crate) fn waitable_set_wait(
4092 self,
4093 store: &mut StoreOpaque,
4094 options: OptionsIndex,
4095 set: u32,
4096 payload: u32,
4097 ) -> Result<u32> {
4098 let &CanonicalOptions {
4099 instance: caller_instance,
4100 ..
4101 } = &self.id().get(store).component().env_component().options[options];
4102 let caller = self.runtime_instance(caller_instance);
4103 let rep = store
4104 .instance_state(self.runtime_instance(caller_instance))
4105 .handle_table()
4106 .waitable_set_rep(set)?;
4107
4108 self.waitable_check(
4109 store,
4110 caller,
4111 WaitableCheck::Wait,
4112 WaitableCheckParams {
4113 set: TableId::new(rep),
4114 options,
4115 payload,
4116 },
4117 )
4118 }
4119
4120 pub(crate) fn waitable_set_poll(
4122 self,
4123 store: &mut StoreOpaque,
4124 options: OptionsIndex,
4125 set: u32,
4126 payload: u32,
4127 ) -> Result<u32> {
4128 let &CanonicalOptions {
4129 instance: caller_instance,
4130 ..
4131 } = &self.id().get(store).component().env_component().options[options];
4132 let caller = self.runtime_instance(caller_instance);
4133 let rep = store
4134 .instance_state(caller)
4135 .handle_table()
4136 .waitable_set_rep(set)?;
4137
4138 self.waitable_check(
4139 store,
4140 caller,
4141 WaitableCheck::Poll,
4142 WaitableCheckParams {
4143 set: TableId::new(rep),
4144 options,
4145 payload,
4146 },
4147 )
4148 }
4149
4150 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4152 let thread_id = store.current_guest_thread()?.thread;
4153 match store
4154 .concurrent_state_mut()?
4155 .get_mut(thread_id)?
4156 .instance_rep
4157 {
4158 Some(r) => Ok(r),
4159 None => bail_bug!("thread should have instance_rep by now"),
4160 }
4161 }
4162
4163 pub(crate) fn thread_new_indirect<T: 'static>(
4165 self,
4166 mut store: StoreContextMut<T>,
4167 runtime_instance: RuntimeComponentInstanceIndex,
4168 start_func_ty: ModuleInternedTypeIndex,
4169 start_func_table_idx: RuntimeTableIndex,
4170 start_func_idx: u32,
4171 context: i32,
4172 ) -> Result<u32> {
4173 log::trace!("creating new thread");
4174
4175 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4176 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4177 else {
4178 bail_bug!("thread.new-indirect start function type should be registered");
4179 };
4180 let callee = instance
4181 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4182 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4183 if callee.type_index(store.0) != start_func_ty {
4184 bail!(Trap::ThreadNewIndirectInvalidType);
4185 }
4186
4187 let token = StoreToken::new(store.as_context_mut());
4188 let start_func = Box::new(
4189 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4190 let old_thread = store.set_thread(guest_thread)?;
4191 log::trace!(
4192 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4193 );
4194
4195 let mut store = token.as_context_mut(store);
4196 let mut params = [ValRaw::i32(context)];
4197 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4200
4201 store.0.set_thread(old_thread)?;
4202
4203 let runtime_instance = self.runtime_instance(runtime_instance);
4204
4205 store
4208 .0
4209 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4210
4211 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4212
4213 log::trace!("explicit thread {guest_thread:?} completed");
4214 let state = store.0.concurrent_state_mut()?;
4215 if let Some(t) = old_thread.guest() {
4216 state.set_thread_running(t.thread)?;
4217 }
4218 log::trace!("thread start: restored {old_thread:?} as current thread");
4219
4220 Ok(())
4221 },
4222 );
4223
4224 let current_thread = store.0.current_guest_thread()?;
4225 let state = store.0.concurrent_state_mut()?;
4226 let parent_task = current_thread.task;
4227
4228 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4229 let thread_id = state.push(new_thread)?;
4230 state.get_mut(parent_task)?.threads.insert(thread_id);
4231
4232 log::trace!("new thread with id {thread_id:?} created");
4233
4234 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4235 }
4236
4237 pub(crate) fn resume_thread(
4238 self,
4239 store: &mut StoreOpaque,
4240 runtime_instance: RuntimeComponentInstanceIndex,
4241 thread_idx: u32,
4242 how: ResumeThread,
4243 ) -> Result<bool> {
4244 let thread_id =
4245 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4246 let state = store.concurrent_state_mut()?;
4247 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4248
4249 if store.current_guest_thread()? == guest_thread {
4250 bail!(Trap::CannotResumeThread);
4251 }
4252
4253 let priority = match how {
4254 ResumeThread::Resume => Priority::Switch,
4255 ResumeThread::ResumeLater => Priority::Low,
4256
4257 ResumeThread::Promote => {
4260 let instance = self.runtime_instance(runtime_instance);
4261 let do_not_enter = store
4262 .instance_state(instance)
4263 .concurrent_state()
4264 .do_not_enter;
4265 return store.concurrent_state_mut()?.promote_work_item_matching(
4266 |item: &WorkItem| match item {
4267 WorkItem::ResumeThread { thread, .. }
4268 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4269
4270 WorkItem::GuestCall {
4271 call: GuestCall { thread, kind },
4272 ..
4273 } => {
4274 *thread == guest_thread
4275 && match kind {
4276 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4280
4281 GuestCallKind::StartExplicit(_) => true,
4284
4285 GuestCallKind::StartImplicit(_) => false,
4289 }
4290 }
4291
4292 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4293 },
4294 );
4295 }
4296 };
4297
4298 let state = store.concurrent_state_mut()?;
4299 let thread = state.get_mut(guest_thread.thread)?;
4300
4301 match &thread.state {
4304 GuestThreadState::NotStartedExplicit(_) => {}
4305 GuestThreadState::Suspended(_) => {
4306 log::trace!("resuming thread {thread_id:?} that was suspended");
4307 state.schedule_suspended_thread(
4308 guest_thread,
4309 self.runtime_instance(runtime_instance),
4310 priority,
4311 )?;
4312 return Ok(true);
4313 }
4314 _ => bail!(Trap::CannotResumeThread),
4315 }
4316 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4317 GuestThreadState::NotStartedExplicit(start_func) => {
4318 log::trace!("starting thread {guest_thread:?}");
4319 let guest_call = WorkItem::GuestCall {
4320 instance: self.runtime_instance(runtime_instance),
4321 call: GuestCall {
4322 thread: guest_thread,
4323 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4324 start_func(store, guest_thread)
4325 })),
4326 },
4327 };
4328 state.push_work_item(guest_call, priority)?;
4329 }
4330 other => {
4331 thread.state = other;
4332 bail_bug!("thread state checked to be resumable above");
4333 }
4334 }
4335 Ok(true)
4336 }
4337
4338 fn add_guest_thread_to_instance_table(
4339 self,
4340 thread_id: TableId<GuestThread>,
4341 store: &mut StoreOpaque,
4342 runtime_instance: RuntimeComponentInstanceIndex,
4343 ) -> Result<u32> {
4344 let (state, counter) =
4345 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4346 let guest_id = state
4347 .thread_handle_table()
4348 .guest_thread_insert(counter, thread_id.rep())?;
4349 store
4350 .concurrent_state_mut()?
4351 .get_mut(thread_id)?
4352 .instance_rep = Some(guest_id);
4353 Ok(guest_id)
4354 }
4355
4356 pub(crate) fn suspension_intrinsic(
4360 self,
4361 store: &mut StoreOpaque,
4362 caller: RuntimeComponentInstanceIndex,
4363 yielding: bool,
4364 to_thread: SuspensionTarget,
4365 ) -> Result<WaitResult> {
4366 let check_suspend = match to_thread {
4367 SuspensionTarget::Promote(thread) => {
4368 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4369 }
4370 SuspensionTarget::Resume(thread) => {
4371 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4372 bail_bug!(
4373 "`resume_thread` should only ever return false \
4374 when `ResumeThread::Promote` is passed to it"
4375 );
4376 }
4377 false
4378 }
4379 SuspensionTarget::None => true,
4380 };
4381
4382 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4383 return if yielding {
4384 Ok(WaitResult::Completed)
4385 } else {
4386 Err(Trap::CannotBlockSyncTask.into())
4387 };
4388 }
4389
4390 let guest_thread = store.current_guest_thread()?;
4391
4392 let reason = if yielding {
4393 SuspendReason::Yielding {
4394 thread: guest_thread,
4395 }
4396 } else {
4397 SuspendReason::ExplicitlySuspending {
4398 thread: guest_thread,
4399 }
4400 };
4401
4402 store.suspend(reason)?;
4403
4404 Ok(WaitResult::Completed)
4405 }
4406
4407 fn waitable_check(
4409 self,
4410 store: &mut StoreOpaque,
4411 caller: RuntimeInstance,
4412 check: WaitableCheck,
4413 params: WaitableCheckParams,
4414 ) -> Result<u32> {
4415 let guest_thread = store.current_guest_thread()?;
4416
4417 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4418
4419 match &check {
4422 WaitableCheck::Wait => {
4423 let set = params.set;
4424
4425 loop {
4430 let state = store.concurrent_state_mut()?;
4431 let task = state.get_mut(guest_thread.task)?;
4432 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4433 || !state.get_mut(set)?.ready.is_empty()
4434 {
4435 break;
4436 }
4437
4438 store.switch_or_trap_if_may_not_suspend(caller)?;
4439
4440 store.suspend(SuspendReason::Waiting {
4441 set,
4442 thread: guest_thread,
4443 })?;
4444 }
4445 }
4446 WaitableCheck::Poll => {}
4447 }
4448
4449 log::trace!(
4450 "waitable check for {guest_thread:?}; set {:?}, part two",
4451 params.set
4452 );
4453
4454 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4456
4457 let (ordinal, handle, result) = match &check {
4458 WaitableCheck::Wait => {
4459 let (event, waitable) = match event {
4460 Some(p) => p,
4461 None => bail_bug!("event expected to be present"),
4462 };
4463 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4464 let (ordinal, result) = event.parts();
4465 (ordinal, handle, result)
4466 }
4467 WaitableCheck::Poll => {
4468 if let Some((event, waitable)) = event {
4469 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4470 let (ordinal, result) = event.parts();
4471 (ordinal, handle, result)
4472 } else {
4473 log::trace!(
4474 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4475 guest_thread.task,
4476 params.set
4477 );
4478 let (ordinal, result) = Event::None.parts();
4479 (ordinal, 0, result)
4480 }
4481 }
4482 };
4483 let memory = self.options_memory_mut(store, params.options);
4484 let ptr = crate::component::func::validate_inbounds_dynamic(
4485 &CanonicalAbiInfo::POINTER_PAIR,
4486 memory,
4487 &ValRaw::u32(params.payload),
4488 )?;
4489 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4490 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4491 Ok(ordinal)
4492 }
4493
4494 pub(crate) fn subtask_cancel(
4496 self,
4497 store: &mut StoreOpaque,
4498 caller_instance: RuntimeComponentInstanceIndex,
4499 async_: bool,
4500 task_id: u32,
4501 ) -> Result<u32> {
4502 let (rep, is_host) = store
4503 .instance_state(self.runtime_instance(caller_instance))
4504 .handle_table()
4505 .subtask_rep(task_id)?;
4506 let waitable = if is_host {
4507 Waitable::Host(TableId::<HostTask>::new(rep))
4508 } else {
4509 Waitable::Guest(TableId::<GuestTask>::new(rep))
4510 };
4511 let concurrent_state = store.concurrent_state_mut()?;
4512
4513 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4514
4515 waitable.trap_if_in_waitable_set(concurrent_state)?;
4516
4517 let needs_block;
4518 if let Waitable::Host(host_task) = waitable {
4519 let state = &mut concurrent_state.get_mut(host_task)?.state;
4520 match state {
4521 HostTaskState::CalleeRunning(handle) => {
4528 handle.abort();
4529 *state = HostTaskState::CalleeCancelling;
4530 needs_block = true;
4531 }
4532
4533 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4536 bail!(Trap::SubtaskCancelAfterTerminal);
4537 }
4538 HostTaskState::CalleeDone { cancelled: false } => {
4539 *state = HostTaskState::CalleeDone { cancelled: true };
4542 needs_block = false;
4543 }
4544
4545 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4548 bail_bug!("invalid states for host callee")
4549 }
4550 }
4551 } else {
4552 let guest_task = TableId::<GuestTask>::new(rep);
4553 let task = concurrent_state.get_mut(guest_task)?;
4554
4555 if task.cancel_requested {
4558 bail!(Trap::SubtaskCancelAfterTerminal);
4559 }
4560
4561 if !task.already_lowered_parameters() {
4562 store.cancel_guest_subtask_without_lowered_parameters(
4563 self.runtime_instance(caller_instance),
4564 guest_task,
4565 )?;
4566 return Ok(Status::StartCancelled as u32);
4567 } else if !task.returned_or_cancelled() {
4568 task.cancel_requested = true;
4575 task.event = Some(Event::Cancelled);
4576 let runtime_instance = task.instance;
4577 for thread in task.threads.clone() {
4578 let thread = QualifiedThreadId {
4579 task: guest_task,
4580 thread,
4581 };
4582 let concurrent_state = store.concurrent_state_mut()?;
4583 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4584
4585 let yield_ = |store: &mut StoreOpaque| {
4586 let state = store.instance_state(runtime_instance).concurrent_state();
4591 let old_do_not_suspend = state.do_not_suspend;
4592 state.do_not_suspend = false;
4593
4594 let caller = store.current_guest_thread()?;
4595
4596 let state = store.concurrent_state_mut()?;
4601 let set = state.get_mut(caller.thread)?.sync_call_set;
4602 waitable.join(state, Some(set))?;
4603
4604 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4605
4606 let state = store.concurrent_state_mut()?;
4607 waitable.join(state, None)?;
4608
4609 store
4610 .instance_state(runtime_instance)
4611 .concurrent_state()
4612 .do_not_suspend = old_do_not_suspend;
4613
4614 Ok::<(), crate::Error>(())
4615 };
4616
4617 match thread_mut.wake_on_cancel.take() {
4618 WakeOnCancel::Waiting(set) => {
4619 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4621 let item = WorkItem::GuestCall {
4622 instance: runtime_instance,
4623 call: GuestCall {
4624 thread,
4625 kind: GuestCallKind::DeliverEvent {
4626 instance,
4627 set: Some(set),
4628 },
4629 },
4630 };
4631 concurrent_state.set_switch_item(item)?;
4632
4633 yield_(store)?;
4634
4635 break;
4636 }
4637 WakeOnCancel::Yielding => {
4638 if concurrent_state.promote_thread_work_item(thread)? {
4639 yield_(store)?;
4640 break;
4641 } else if store
4642 .instance_state(runtime_instance)
4643 .concurrent_state()
4644 .pending
4645 .contains_key(&thread)
4646 {
4647 store
4657 .concurrent_state_mut()?
4658 .get_mut(thread.thread)?
4659 .wake_on_cancel = WakeOnCancel::Yielding;
4660 } else {
4661 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4662 }
4663 }
4664 WakeOnCancel::None => {}
4665 }
4666 }
4667
4668 needs_block = !store
4671 .concurrent_state_mut()?
4672 .get_mut(guest_task)?
4673 .returned_or_cancelled()
4674 } else {
4675 needs_block = false;
4676 }
4677 };
4678
4679 if needs_block {
4683 if async_ {
4684 return Ok(BLOCKED);
4685 }
4686
4687 let old_next_switch_item = if is_host {
4697 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4698 } else {
4699 None
4700 };
4701
4702 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4705
4706 if let Some(item) = old_next_switch_item {
4707 let state = store.concurrent_state_mut()?;
4708 state.restore_next_switch_item(item)?;
4709 }
4710
4711 }
4713
4714 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4715 if let Some(Event::Subtask {
4716 status: status @ (Status::Returned | Status::ReturnCancelled),
4717 }) = event
4718 {
4719 Ok(status as u32)
4720 } else {
4721 bail!(Trap::SubtaskCancelAfterTerminal);
4722 }
4723 }
4724}
4725
4726pub trait VMComponentAsyncStore {
4734 unsafe fn prepare_call(
4740 &mut self,
4741 instance: Instance,
4742 memory: *mut VMMemoryDefinition,
4743 start: NonNull<VMFuncRef>,
4744 return_: NonNull<VMFuncRef>,
4745 caller_instance: RuntimeComponentInstanceIndex,
4746 callee_instance: RuntimeComponentInstanceIndex,
4747 task_return_type: TypeTupleIndex,
4748 callee_async: bool,
4749 string_encoding: StringEncoding,
4750 result_count: u32,
4751 storage: *mut ValRaw,
4752 storage_len: usize,
4753 ) -> Result<()>;
4754
4755 unsafe fn start_call(
4758 &mut self,
4759 instance: Instance,
4760 callback: *mut VMFuncRef,
4761 post_return: *mut VMFuncRef,
4762 callee: NonNull<VMFuncRef>,
4763 param_count: u32,
4764 result_count: u32,
4765 flags: u32,
4766 storage: *mut MaybeUninit<ValRaw>,
4767 storage_len: usize,
4768 ) -> Result<()>;
4769
4770 fn future_write(
4772 &mut self,
4773 instance: Instance,
4774 caller: RuntimeComponentInstanceIndex,
4775 ty: TypeFutureTableIndex,
4776 options: OptionsIndex,
4777 future: u32,
4778 address: u32,
4779 ) -> Result<u32>;
4780
4781 fn future_read(
4783 &mut self,
4784 instance: Instance,
4785 caller: RuntimeComponentInstanceIndex,
4786 ty: TypeFutureTableIndex,
4787 options: OptionsIndex,
4788 future: u32,
4789 address: u32,
4790 ) -> Result<u32>;
4791
4792 fn future_drop_writable(
4794 &mut self,
4795 instance: Instance,
4796 ty: TypeFutureTableIndex,
4797 writer: u32,
4798 ) -> Result<()>;
4799
4800 fn stream_write(
4802 &mut self,
4803 instance: Instance,
4804 caller: RuntimeComponentInstanceIndex,
4805 ty: TypeStreamTableIndex,
4806 options: OptionsIndex,
4807 stream: u32,
4808 address: u32,
4809 count: u32,
4810 ) -> Result<u32>;
4811
4812 fn stream_read(
4814 &mut self,
4815 instance: Instance,
4816 caller: RuntimeComponentInstanceIndex,
4817 ty: TypeStreamTableIndex,
4818 options: OptionsIndex,
4819 stream: u32,
4820 address: u32,
4821 count: u32,
4822 ) -> Result<u32>;
4823
4824 fn flat_stream_write(
4827 &mut self,
4828 instance: Instance,
4829 caller: RuntimeComponentInstanceIndex,
4830 ty: TypeStreamTableIndex,
4831 options: OptionsIndex,
4832 payload_size: u32,
4833 payload_align: u32,
4834 stream: u32,
4835 address: u32,
4836 count: u32,
4837 ) -> Result<u32>;
4838
4839 fn flat_stream_read(
4842 &mut self,
4843 instance: Instance,
4844 caller: RuntimeComponentInstanceIndex,
4845 ty: TypeStreamTableIndex,
4846 options: OptionsIndex,
4847 payload_size: u32,
4848 payload_align: u32,
4849 stream: u32,
4850 address: u32,
4851 count: u32,
4852 ) -> Result<u32>;
4853
4854 fn stream_drop_writable(
4856 &mut self,
4857 instance: Instance,
4858 ty: TypeStreamTableIndex,
4859 writer: u32,
4860 ) -> Result<()>;
4861
4862 fn error_context_debug_message(
4864 &mut self,
4865 instance: Instance,
4866 ty: TypeComponentLocalErrorContextTableIndex,
4867 options: OptionsIndex,
4868 err_ctx_handle: u32,
4869 debug_msg_address: u32,
4870 ) -> Result<()>;
4871
4872 fn thread_new_indirect(
4874 &mut self,
4875 instance: Instance,
4876 caller: RuntimeComponentInstanceIndex,
4877 func_ty_idx: ModuleInternedTypeIndex,
4878 start_func_table_idx: RuntimeTableIndex,
4879 start_func_idx: u32,
4880 context: i32,
4881 ) -> Result<u32>;
4882}
4883
4884impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4886 unsafe fn prepare_call(
4887 &mut self,
4888 instance: Instance,
4889 memory: *mut VMMemoryDefinition,
4890 start: NonNull<VMFuncRef>,
4891 return_: NonNull<VMFuncRef>,
4892 caller_instance: RuntimeComponentInstanceIndex,
4893 callee_instance: RuntimeComponentInstanceIndex,
4894 task_return_type: TypeTupleIndex,
4895 callee_async: bool,
4896 string_encoding: StringEncoding,
4897 result_count_or_max_if_async: u32,
4898 storage: *mut ValRaw,
4899 storage_len: usize,
4900 ) -> Result<()> {
4901 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4905
4906 unsafe {
4907 instance.prepare_call(
4908 StoreContextMut(self),
4909 start,
4910 return_,
4911 caller_instance,
4912 callee_instance,
4913 task_return_type,
4914 callee_async,
4915 memory,
4916 string_encoding,
4917 match result_count_or_max_if_async {
4918 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4919 params,
4920 has_result: false,
4921 },
4922 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4923 params,
4924 has_result: true,
4925 },
4926 result_count => CallerInfo::Sync {
4927 params,
4928 result_count,
4929 },
4930 },
4931 )
4932 }
4933 }
4934
4935 unsafe fn start_call(
4936 &mut self,
4937 instance: Instance,
4938 callback: *mut VMFuncRef,
4939 post_return: *mut VMFuncRef,
4940 callee: NonNull<VMFuncRef>,
4941 param_count: u32,
4942 result_count: u32,
4943 flags: u32,
4944 storage: *mut MaybeUninit<ValRaw>,
4945 storage_len: usize,
4946 ) -> Result<()> {
4947 unsafe {
4948 instance.start_call(
4949 StoreContextMut(self),
4950 callback,
4951 post_return,
4952 callee,
4953 param_count,
4954 result_count,
4955 flags,
4956 core::slice::from_raw_parts_mut(storage, storage_len),
4960 )
4961 }
4962 }
4963
4964 fn future_write(
4965 &mut self,
4966 instance: Instance,
4967 caller: RuntimeComponentInstanceIndex,
4968 ty: TypeFutureTableIndex,
4969 options: OptionsIndex,
4970 future: u32,
4971 address: u32,
4972 ) -> Result<u32> {
4973 instance
4974 .guest_write(
4975 StoreContextMut(self),
4976 caller,
4977 TransmitIndex::Future(ty),
4978 options,
4979 None,
4980 future,
4981 address,
4982 1,
4983 )
4984 .map(|result| result.encode())
4985 }
4986
4987 fn future_read(
4988 &mut self,
4989 instance: Instance,
4990 caller: RuntimeComponentInstanceIndex,
4991 ty: TypeFutureTableIndex,
4992 options: OptionsIndex,
4993 future: u32,
4994 address: u32,
4995 ) -> Result<u32> {
4996 instance
4997 .guest_read(
4998 StoreContextMut(self),
4999 caller,
5000 TransmitIndex::Future(ty),
5001 options,
5002 None,
5003 future,
5004 address,
5005 1,
5006 )
5007 .map(|result| result.encode())
5008 }
5009
5010 fn stream_write(
5011 &mut self,
5012 instance: Instance,
5013 caller: RuntimeComponentInstanceIndex,
5014 ty: TypeStreamTableIndex,
5015 options: OptionsIndex,
5016 stream: u32,
5017 address: u32,
5018 count: u32,
5019 ) -> Result<u32> {
5020 instance
5021 .guest_write(
5022 StoreContextMut(self),
5023 caller,
5024 TransmitIndex::Stream(ty),
5025 options,
5026 None,
5027 stream,
5028 address,
5029 count,
5030 )
5031 .map(|result| result.encode())
5032 }
5033
5034 fn stream_read(
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_read(
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 future_drop_writable(
5059 &mut self,
5060 instance: Instance,
5061 ty: TypeFutureTableIndex,
5062 writer: u32,
5063 ) -> Result<()> {
5064 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5065 }
5066
5067 fn flat_stream_write(
5068 &mut self,
5069 instance: Instance,
5070 caller: RuntimeComponentInstanceIndex,
5071 ty: TypeStreamTableIndex,
5072 options: OptionsIndex,
5073 payload_size: u32,
5074 payload_align: u32,
5075 stream: u32,
5076 address: u32,
5077 count: u32,
5078 ) -> Result<u32> {
5079 instance
5080 .guest_write(
5081 StoreContextMut(self),
5082 caller,
5083 TransmitIndex::Stream(ty),
5084 options,
5085 Some(FlatAbi {
5086 size: payload_size,
5087 align: payload_align,
5088 }),
5089 stream,
5090 address,
5091 count,
5092 )
5093 .map(|result| result.encode())
5094 }
5095
5096 fn flat_stream_read(
5097 &mut self,
5098 instance: Instance,
5099 caller: RuntimeComponentInstanceIndex,
5100 ty: TypeStreamTableIndex,
5101 options: OptionsIndex,
5102 payload_size: u32,
5103 payload_align: u32,
5104 stream: u32,
5105 address: u32,
5106 count: u32,
5107 ) -> Result<u32> {
5108 instance
5109 .guest_read(
5110 StoreContextMut(self),
5111 caller,
5112 TransmitIndex::Stream(ty),
5113 options,
5114 Some(FlatAbi {
5115 size: payload_size,
5116 align: payload_align,
5117 }),
5118 stream,
5119 address,
5120 count,
5121 )
5122 .map(|result| result.encode())
5123 }
5124
5125 fn stream_drop_writable(
5126 &mut self,
5127 instance: Instance,
5128 ty: TypeStreamTableIndex,
5129 writer: u32,
5130 ) -> Result<()> {
5131 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5132 }
5133
5134 fn error_context_debug_message(
5135 &mut self,
5136 instance: Instance,
5137 ty: TypeComponentLocalErrorContextTableIndex,
5138 options: OptionsIndex,
5139 err_ctx_handle: u32,
5140 debug_msg_address: u32,
5141 ) -> Result<()> {
5142 instance.error_context_debug_message(
5143 StoreContextMut(self),
5144 ty,
5145 options,
5146 err_ctx_handle,
5147 debug_msg_address,
5148 )
5149 }
5150
5151 fn thread_new_indirect(
5152 &mut self,
5153 instance: Instance,
5154 caller: RuntimeComponentInstanceIndex,
5155 func_ty_idx: ModuleInternedTypeIndex,
5156 start_func_table_idx: RuntimeTableIndex,
5157 start_func_idx: u32,
5158 context: i32,
5159 ) -> Result<u32> {
5160 instance.thread_new_indirect(
5161 StoreContextMut(self),
5162 caller,
5163 func_ty_idx,
5164 start_func_table_idx,
5165 start_func_idx,
5166 context,
5167 )
5168 }
5169}
5170
5171type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5172
5173async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5176where
5177 F: Future,
5178{
5179 let mut future = pin!(future);
5180 future::poll_fn(|cx| {
5181 let old_thread = match tls::get(|store| store.set_thread(task)) {
5182 Ok(thread) => thread,
5183 Err(error) => return Poll::Ready(Err(error)),
5184 };
5185 let result = future.as_mut().poll(cx);
5186 match tls::get(|store| store.set_thread(old_thread)) {
5187 Ok(_) => result.map(Ok),
5188 Err(error) => Poll::Ready(Err(error)),
5189 }
5190 })
5191 .await
5192}
5193
5194pub(crate) struct HostTask {
5198 common: WaitableCommon,
5199
5200 call_context: CallContext,
5203
5204 state: HostTaskState,
5205
5206 group: TaskGroupId,
5207}
5208
5209enum HostTaskState {
5210 CalleeStarted,
5215
5216 CalleeRunning(JoinHandle),
5221
5222 CalleeCancelling,
5226
5227 CalleeFinished(LiftedResult),
5231
5232 CalleeDone { cancelled: bool },
5235}
5236
5237impl HostTask {
5238 fn new(
5239 concurrent_state: &mut ConcurrentState,
5240 state: HostTaskState,
5241 caller: QualifiedThreadId,
5242 ) -> Result<Self> {
5243 let group = concurrent_state.get_mut(caller.task)?.group;
5244 concurrent_state.increment_group_ref_count(group)?;
5245
5246 Ok(Self {
5247 common: WaitableCommon::default(),
5248 call_context: CallContext::default(),
5249 state,
5250 group,
5251 })
5252 }
5253}
5254
5255impl TableDebug for HostTask {
5256 fn type_name() -> &'static str {
5257 "HostTask"
5258 }
5259}
5260
5261type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5262
5263enum Caller {
5265 Host {
5267 tx: Option<oneshot::Sender<LiftedResult>>,
5269 caller: Option<TableId<HostTask>>,
5273 },
5274 Guest {
5276 thread: QualifiedThreadId,
5278 },
5279}
5280
5281struct LiftResult {
5284 lift: RawLift,
5285 ty: TypeTupleIndex,
5286 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5287 string_encoding: StringEncoding,
5288}
5289
5290#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5295pub(crate) struct QualifiedThreadId {
5296 task: TableId<GuestTask>,
5297 thread: TableId<GuestThread>,
5298}
5299
5300impl QualifiedThreadId {
5301 fn qualify(
5302 state: &mut ConcurrentState,
5303 thread: TableId<GuestThread>,
5304 ) -> Result<QualifiedThreadId> {
5305 Ok(QualifiedThreadId {
5306 task: state.get_mut(thread)?.parent_task,
5307 thread,
5308 })
5309 }
5310}
5311
5312impl fmt::Debug for QualifiedThreadId {
5313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5314 f.debug_tuple("QualifiedThreadId")
5315 .field(&self.task.rep())
5316 .field(&self.thread.rep())
5317 .finish()
5318 }
5319}
5320
5321enum GuestThreadState {
5322 NotStartedImplicit,
5323 NotStartedExplicit(
5324 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5325 ),
5326 Running,
5327 Suspended(StoreFiber<'static>),
5328 Ready {
5329 fiber: StoreFiber<'static>,
5330 },
5331 Completed,
5332}
5333
5334impl GuestThreadState {
5335 fn check_no_fiber(&self) -> Result<()> {
5336 match self {
5337 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5338 Self::NotStartedImplicit
5339 | Self::NotStartedExplicit(_)
5340 | Self::Running
5341 | Self::Completed => Ok(()),
5342 }
5343 }
5344}
5345
5346impl fmt::Debug for GuestThreadState {
5347 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5348 match self {
5349 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5350 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5351 Self::Running => f.debug_tuple("Running").finish(),
5352 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5353 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5354 Self::Completed => f.debug_tuple("Completed").finish(),
5355 }
5356 }
5357}
5358
5359#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5360enum WakeOnCancel {
5361 None,
5362 Waiting(TableId<WaitableSet>),
5363 Yielding,
5364}
5365
5366impl WakeOnCancel {
5367 fn is_none(self) -> bool {
5368 matches!(self, WakeOnCancel::None)
5369 }
5370
5371 fn replace(&mut self, other: WakeOnCancel) -> Self {
5372 let old = *self;
5373 *self = other;
5374 old
5375 }
5376
5377 fn take(&mut self) -> Self {
5378 self.replace(WakeOnCancel::None)
5379 }
5380}
5381
5382pub struct GuestThread {
5383 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5386 parent_task: TableId<GuestTask>,
5388 wake_on_cancel: WakeOnCancel,
5391 state: GuestThreadState,
5393 instance_rep: Option<u32>,
5396 sync_call_set: TableId<WaitableSet>,
5398 old_do_not_suspend: Option<bool>,
5401}
5402
5403impl GuestThread {
5404 fn from_instance(
5407 state: Pin<&mut ComponentInstance>,
5408 caller_instance: RuntimeComponentInstanceIndex,
5409 guest_thread: u32,
5410 ) -> Result<TableId<Self>> {
5411 let rep = state.instance_states().0[caller_instance]
5412 .thread_handle_table()
5413 .guest_thread_rep(guest_thread)?;
5414 Ok(TableId::new(rep))
5415 }
5416
5417 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5418 let sync_call_set = state.push(WaitableSet {
5419 is_sync_call_set: true,
5420 ..WaitableSet::default()
5421 })?;
5422 Ok(Self {
5423 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5424 parent_task,
5425 wake_on_cancel: WakeOnCancel::None,
5426 state: GuestThreadState::NotStartedImplicit,
5427 instance_rep: None,
5428 sync_call_set,
5429 old_do_not_suspend: None,
5430 })
5431 }
5432
5433 fn new_explicit(
5434 state: &mut ConcurrentState,
5435 parent_task: TableId<GuestTask>,
5436 start_func: Box<
5437 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5438 >,
5439 ) -> Result<Self> {
5440 state.increment_task_ref_count(parent_task)?;
5441 let sync_call_set = state.push(WaitableSet {
5442 is_sync_call_set: true,
5443 ..WaitableSet::default()
5444 })?;
5445 Ok(Self {
5446 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5447 parent_task,
5448 wake_on_cancel: WakeOnCancel::None,
5449 state: GuestThreadState::NotStartedExplicit(start_func),
5450 instance_rep: None,
5451 sync_call_set,
5452 old_do_not_suspend: None,
5453 })
5454 }
5455}
5456
5457impl TableDebug for GuestThread {
5458 fn type_name() -> &'static str {
5459 "GuestThread"
5460 }
5461}
5462
5463enum SyncResult {
5464 NotProduced,
5465 Produced(Option<ValRaw>),
5466 Taken,
5467}
5468
5469impl SyncResult {
5470 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5471 Ok(match mem::replace(self, SyncResult::Taken) {
5472 SyncResult::NotProduced => None,
5473 SyncResult::Produced(val) => Some(val),
5474 SyncResult::Taken => {
5475 bail_bug!("attempted to take a synchronous result that was already taken")
5476 }
5477 })
5478 }
5479}
5480
5481pub(crate) struct GuestTask {
5483 common: WaitableCommon,
5485 lower_params: Option<RawLower>,
5487 lift_result: Option<LiftResult>,
5489 result: Option<LiftedResult>,
5492 callback: Option<CallbackFn>,
5495 caller: Caller,
5497 call_context: CallContext,
5502 sync_result: SyncResult,
5505 cancel_request_delivered: bool,
5509 cancel_requested: bool,
5514 starting_sent: bool,
5517 instance: RuntimeInstance,
5524 event: Option<Event>,
5526 exited: bool,
5528 threads: HashSet<TableId<GuestThread>>,
5530 async_typed: bool,
5533 async_lifted: bool,
5536 decremented_interesting_task_count: bool,
5539 group: TaskGroupId,
5541 ref_count: usize,
5546}
5547
5548impl GuestTask {
5549 fn already_lowered_parameters(&self) -> bool {
5550 self.lower_params.is_none()
5552 }
5553
5554 fn returned_or_cancelled(&self) -> bool {
5555 self.lift_result.is_none()
5557 }
5558
5559 fn new(
5560 state: &mut ConcurrentState,
5561 lower_params: RawLower,
5562 lift_result: LiftResult,
5563 caller: Caller,
5564 callback: Option<CallbackFn>,
5565 instance: RuntimeInstance,
5566 async_typed: bool,
5567 async_lifted: bool,
5568 ) -> Result<QualifiedThreadId> {
5569 let group = match caller {
5570 Caller::Guest { thread } => {
5571 let group = state.get_mut(thread.task)?.group;
5572 state.increment_group_ref_count(group)?;
5573 group
5574 }
5575 Caller::Host { .. } => state.make_task_group()?,
5576 };
5577
5578 let task = state.push(Self {
5579 common: WaitableCommon::default(),
5580 lower_params: Some(lower_params),
5581 lift_result: Some(lift_result),
5582 result: None,
5583 callback,
5584 caller,
5585 call_context: CallContext::default(),
5586 sync_result: SyncResult::NotProduced,
5587 cancel_request_delivered: false,
5588 cancel_requested: false,
5589 starting_sent: false,
5590 instance,
5591 event: None,
5592 exited: false,
5593 threads: HashSet::new(),
5594 async_typed,
5595 async_lifted,
5596 decremented_interesting_task_count: false,
5597 group,
5598 ref_count: 2,
5601 })?;
5602 let new_thread = GuestThread::new_implicit(state, task)?;
5603 let thread = state.push(new_thread)?;
5604 state.get_mut(task)?.threads.insert(thread);
5605 state.interesting_tasks += 1;
5606 let thread = QualifiedThreadId { task, thread };
5607 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5608 Ok(thread)
5609 }
5610}
5611
5612impl TableDebug for GuestTask {
5613 fn type_name() -> &'static str {
5614 "GuestTask"
5615 }
5616}
5617
5618#[derive(Default)]
5620struct WaitableCommon {
5621 event: Option<Event>,
5623 set: Option<TableId<WaitableSet>>,
5625 handle: Option<u32>,
5627}
5628
5629#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5631enum Waitable {
5632 Host(TableId<HostTask>),
5634 Guest(TableId<GuestTask>),
5636 Transmit(TableId<TransmitHandle>),
5638}
5639
5640impl Waitable {
5641 fn from_instance(
5644 state: Pin<&mut ComponentInstance>,
5645 caller_instance: RuntimeComponentInstanceIndex,
5646 waitable: u32,
5647 ) -> Result<Self> {
5648 use crate::runtime::vm::component::Waitable;
5649
5650 let (waitable, kind) = state.instance_states().0[caller_instance]
5651 .handle_table()
5652 .waitable_rep(waitable)?;
5653
5654 Ok(match kind {
5655 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5656 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5657 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5658 })
5659 }
5660
5661 fn rep(&self) -> u32 {
5663 match self {
5664 Self::Host(id) => id.rep(),
5665 Self::Guest(id) => id.rep(),
5666 Self::Transmit(id) => id.rep(),
5667 }
5668 }
5669
5670 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5674 log::trace!("waitable {self:?} join set {set:?}");
5675
5676 let old = mem::replace(&mut self.common(state)?.set, set);
5677
5678 if let Some(old) = old {
5679 match *self {
5680 Waitable::Host(id) => state.remove_child(id, old),
5681 Waitable::Guest(id) => state.remove_child(id, old),
5682 Waitable::Transmit(id) => state.remove_child(id, old),
5683 }?;
5684
5685 state.get_mut(old)?.ready.remove(self);
5686 }
5687
5688 if let Some(set) = set {
5689 match *self {
5690 Waitable::Host(id) => state.add_child(id, set),
5691 Waitable::Guest(id) => state.add_child(id, set),
5692 Waitable::Transmit(id) => state.add_child(id, set),
5693 }?;
5694
5695 if self.common(state)?.event.is_some() {
5696 self.mark_ready(state)?;
5697 }
5698 }
5699
5700 Ok(())
5701 }
5702
5703 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5705 Ok(match self {
5706 Self::Host(id) => &mut state.get_mut(*id)?.common,
5707 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5708 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5709 })
5710 }
5711
5712 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5718 if self.common(state)?.set.is_some() {
5719 bail!(Trap::WaitableSyncAndAsync);
5720 }
5721 Ok(())
5722 }
5723
5724 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5730 if let Some(set) = self.common(state)?.set
5731 && state.get_mut(set)?.is_sync_call_set
5732 {
5733 bail!(Trap::WaitableSyncAndAsync);
5734 }
5735 Ok(())
5736 }
5737
5738 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5742 log::trace!("set event for {self:?}: {event:?}");
5743 self.common(state)?.event = event;
5744 self.mark_ready(state)
5745 }
5746
5747 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5749 let common = self.common(state)?;
5750 let event = common.event.take();
5751 if let Some(set) = self.common(state)?.set {
5752 state.get_mut(set)?.ready.remove(self);
5753 }
5754
5755 Ok(event)
5756 }
5757
5758 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5762 if let Some(set) = self.common(state)?.set {
5763 state.get_mut(set)?.ready.insert(*self);
5764 state.wake_waiter(set)?;
5765 }
5766 Ok(())
5767 }
5768
5769 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5772 match self {
5773 Self::Host(task) => state.delete_host_task(*task),
5774 Self::Guest(task) => state.decrement_task_ref_count(*task),
5775 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5776 }
5777 }
5778}
5779
5780impl fmt::Debug for Waitable {
5781 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5782 match self {
5783 Self::Host(id) => write!(f, "{id:?}"),
5784 Self::Guest(id) => write!(f, "{id:?}"),
5785 Self::Transmit(id) => write!(f, "{id:?}"),
5786 }
5787 }
5788}
5789
5790#[derive(Default)]
5792struct WaitableSet {
5793 ready: BTreeSet<Waitable>,
5795 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5797 num_waiting: usize,
5800 is_sync_call_set: bool,
5803}
5804
5805impl WaitableSet {
5806 fn stop_waiting(&mut self) -> Result<()> {
5808 self.num_waiting = match self.num_waiting.checked_sub(1) {
5809 Some(n) => n,
5810 None => bail_bug!("waiter not accounted for in waitable set"),
5811 };
5812 Ok(())
5813 }
5814}
5815
5816impl TableDebug for WaitableSet {
5817 fn type_name() -> &'static str {
5818 "WaitableSet"
5819 }
5820}
5821
5822type RawLower =
5824 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5825
5826type RawLift = Box<
5828 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5829>;
5830
5831type LiftedResult = Box<dyn Any + Send + Sync>;
5835
5836struct DummyResult;
5839
5840#[derive(Default)]
5842pub struct ConcurrentInstanceState {
5843 backpressure: u16,
5845 do_not_enter: bool,
5847 do_not_suspend: bool,
5850 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5853}
5854
5855impl ConcurrentInstanceState {
5856 pub fn pending_is_empty(&self) -> bool {
5857 self.pending.is_empty()
5858 }
5859}
5860
5861#[derive(Debug, Copy, Clone)]
5862pub(crate) enum CurrentThread {
5863 Guest(QualifiedThreadId),
5866 Host(TableId<HostTask>),
5868 DeferredHost(QualifiedThreadId),
5871 None,
5874}
5875
5876impl CurrentThread {
5877 fn guest(&self) -> Option<&QualifiedThreadId> {
5878 match self {
5879 Self::Guest(id) => Some(id),
5880 _ => None,
5881 }
5882 }
5883
5884 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5885 match self {
5886 Self::Guest(id) => Some(id.task),
5887 _ => None,
5888 }
5889 }
5890
5891 fn is_none(&self) -> bool {
5892 matches!(self, Self::None)
5893 }
5894}
5895
5896impl From<QualifiedThreadId> for CurrentThread {
5897 fn from(id: QualifiedThreadId) -> Self {
5898 Self::Guest(id)
5899 }
5900}
5901
5902impl From<TableId<HostTask>> for CurrentThread {
5903 fn from(id: TableId<HostTask>) -> Self {
5904 Self::Host(id)
5905 }
5906}
5907
5908enum Priority {
5909 Switch,
5910 High,
5911 Low,
5912}
5913
5914pub struct ConcurrentState {
5916 unforced_current_thread: CurrentThread,
5922
5923 deferred_host_call_context: Option<CallContext>,
5929
5930 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5935 table: AlwaysMut<ResourceTable>,
5937 switch_item: Option<WorkItem>,
5945 next_switch_item: Option<WorkItem>,
5951 high_priority: VecDeque<WorkItem>,
5953 low_priority: VecDeque<WorkItem>,
5955 suspend_reason: Option<SuspendReason>,
5959 worker: Option<StoreFiber<'static>>,
5963 worker_item: Option<WorkerItem>,
5965
5966 global_error_context_ref_counts:
5979 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5980
5981 interesting_tasks: usize,
5994
5995 interesting_tasks_empty_waker: Option<Waker>,
5999
6000 ready_for_concurrent_call_waker: Option<Waker>,
6005
6006 event_loop_running: bool,
6008
6009 saved_next_switch_items: Vec<Option<WorkItem>>,
6012
6013 #[cfg(feature = "task-group-hook")]
6015 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6016}
6017
6018impl Default for ConcurrentState {
6019 fn default() -> Self {
6020 Self {
6021 unforced_current_thread: CurrentThread::None,
6022 deferred_host_call_context: None,
6023 table: AlwaysMut::new(ResourceTable::new()),
6024 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6025 switch_item: None,
6026 next_switch_item: None,
6027 high_priority: VecDeque::new(),
6028 low_priority: VecDeque::new(),
6029 suspend_reason: None,
6030 worker: None,
6031 worker_item: None,
6032 global_error_context_ref_counts: BTreeMap::new(),
6033 interesting_tasks: 0,
6034 interesting_tasks_empty_waker: None,
6035 ready_for_concurrent_call_waker: None,
6036 event_loop_running: false,
6037 saved_next_switch_items: Vec::new(),
6038 #[cfg(feature = "task-group-hook")]
6039 task_group_hook: None,
6040 }
6041 }
6042}
6043
6044impl ConcurrentState {
6045 pub(crate) fn take_fibers_and_futures(
6062 &mut self,
6063 fibers: &mut Vec<StoreFiber<'static>>,
6064 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6065 ) {
6066 let ConcurrentState {
6067 table,
6068 worker,
6069 switch_item,
6070 next_switch_item,
6071 high_priority,
6072 low_priority,
6073 saved_next_switch_items,
6074 futures: pending_futures,
6075 worker_item: _,
6076 unforced_current_thread: _,
6077 deferred_host_call_context: _,
6078 suspend_reason: _,
6079 global_error_context_ref_counts: _,
6080 interesting_tasks: _,
6081 interesting_tasks_empty_waker: _,
6082 ready_for_concurrent_call_waker: _,
6083 event_loop_running: _,
6084 #[cfg(feature = "task-group-hook")]
6085 task_group_hook: _,
6086 } = self;
6087
6088 let mut items = Vec::new();
6089 for (_, entry) in table.get_mut().iter_mut() {
6090 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6091 for mode in mem::take(&mut set.waiting).into_values() {
6092 match mode {
6093 WaitMode::Fiber(fiber) => {
6094 fibers.push(fiber);
6095 }
6096 WaitMode::Callback(_) => {}
6097 }
6098 }
6099 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6100 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6101 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6102 fibers.push(fiber);
6103 }
6104 GuestThreadState::NotStartedImplicit
6105 | GuestThreadState::NotStartedExplicit(_)
6106 | GuestThreadState::Running
6107 | GuestThreadState::Completed => {}
6108 }
6109 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6110 if let Some(item) = item.take() {
6111 items.push(item);
6112 }
6113 }
6114 }
6115
6116 if let Some(fiber) = worker.take() {
6117 fibers.push(fiber);
6118 }
6119
6120 let mut handle_item = |item| match item {
6121 WorkItem::ResumeFiber { fiber, .. } => {
6122 fibers.push(fiber);
6123 }
6124 WorkItem::PushFuture(future) => {
6125 pending_futures
6126 .get_mut()
6127 .as_mut()
6128 .unwrap()
6129 .push(future.into_inner());
6130 }
6131 WorkItem::ResumeThread { .. }
6132 | WorkItem::GuestCall { .. }
6133 | WorkItem::WorkerFunction(_) => {}
6134 };
6135
6136 for item in items {
6137 handle_item(item);
6138 }
6139 if let Some(item) = switch_item.take() {
6140 handle_item(item);
6141 }
6142 if let Some(item) = next_switch_item.take() {
6143 handle_item(item);
6144 }
6145 for item in mem::take(high_priority) {
6146 handle_item(item);
6147 }
6148 for item in mem::take(low_priority) {
6149 handle_item(item);
6150 }
6151 for item in mem::take(saved_next_switch_items)
6152 .into_iter()
6153 .filter_map(|v| v)
6154 {
6155 handle_item(item);
6156 }
6157
6158 if let Some(them) = pending_futures.get_mut().take() {
6159 futures.push(them);
6160 }
6161 }
6162
6163 #[cfg(feature = "gc")]
6164 pub(crate) fn trace_fiber_roots(
6165 &mut self,
6166 modules: &ModuleRegistry,
6167 unwind: &dyn Unwind,
6168 gc_roots_list: &mut GcRootsList,
6169 ) {
6170 let ConcurrentState {
6171 table,
6172 worker,
6173 switch_item,
6174 next_switch_item,
6175 high_priority,
6176 low_priority,
6177 saved_next_switch_items,
6178
6179 futures: _,
6183
6184 worker_item: _,
6186 unforced_current_thread: _,
6187 deferred_host_call_context: _,
6188 suspend_reason: _,
6189 global_error_context_ref_counts: _,
6190 interesting_tasks: _,
6191 interesting_tasks_empty_waker: _,
6192 ready_for_concurrent_call_waker: _,
6193 event_loop_running: _,
6194 #[cfg(feature = "task-group-hook")]
6195 task_group_hook: _,
6196 } = self;
6197
6198 for (_, entry) in table.get_mut().iter_mut() {
6199 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6200 for mode in set.waiting.values_mut() {
6201 match mode {
6202 WaitMode::Fiber(fiber) => {
6203 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6204 }
6205 WaitMode::Callback(_) => {}
6206 }
6207 }
6208 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6209 match &mut thread.state {
6210 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6211 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6212 }
6213 GuestThreadState::NotStartedImplicit
6214 | GuestThreadState::NotStartedExplicit(_)
6215 | GuestThreadState::Running
6216 | GuestThreadState::Completed => {}
6217 }
6218 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6219 entry.downcast_mut::<Option<WorkItem>>()
6220 {
6221 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6222 }
6223 }
6224
6225 if let Some(fiber) = worker {
6226 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6227 }
6228
6229 let mut handle_item = |item: &mut WorkItem| match item {
6230 WorkItem::ResumeFiber { fiber, .. } => {
6231 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6232 }
6233 WorkItem::PushFuture(_future) => {
6234 }
6237 WorkItem::ResumeThread { .. }
6238 | WorkItem::GuestCall { .. }
6239 | WorkItem::WorkerFunction(_) => {}
6240 };
6241
6242 if let Some(item) = switch_item {
6243 handle_item(item);
6244 }
6245 if let Some(item) = next_switch_item {
6246 handle_item(item);
6247 }
6248 for item in high_priority {
6249 handle_item(item);
6250 }
6251 for item in low_priority {
6252 handle_item(item);
6253 }
6254 for item in saved_next_switch_items
6255 .iter_mut()
6256 .filter_map(|v| v.as_mut())
6257 {
6258 handle_item(item);
6259 }
6260 }
6261
6262 fn schedule_suspended_thread(
6263 &mut self,
6264 thread: QualifiedThreadId,
6265 instance: RuntimeInstance,
6266 priority: Priority,
6267 ) -> Result<()> {
6268 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6270 bail_bug!("switch item already set");
6271 }
6272 let slot = &mut self.get_mut(thread.thread)?.state;
6273 if !matches!(slot, GuestThreadState::Suspended(_)) {
6274 bail!(Trap::CannotResumeThread);
6275 }
6276 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6277 else {
6278 unreachable!()
6279 };
6280 let item = WorkItem::ResumeFiber {
6281 instance,
6282 thread,
6283 fiber,
6284 };
6285 match priority {
6286 Priority::Switch => self.switch_item = Some(item),
6287 Priority::High => self.push_high_priority(item),
6288 Priority::Low => self.push_low_priority(item),
6289 }
6290 Ok(())
6291 }
6292
6293 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6294 let slot = &mut self.get_mut(thread)?.state;
6295 slot.check_no_fiber()?;
6296 *slot = GuestThreadState::Running;
6297 Ok(())
6298 }
6299
6300 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6301 let saved = self.push(None::<WorkItem>)?;
6304 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6305 *slot = self.next_switch_item.take();
6306 Ok(saved)
6307 }
6308
6309 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6310 if self.next_switch_item.is_some() {
6311 bail_bug!("next switch item already set when restoring");
6312 }
6313 self.next_switch_item = self.delete(saved)?;
6314 Ok(())
6315 }
6316
6317 fn take_callback_waiter(
6318 &mut self,
6319 set: TableId<WaitableSet>,
6320 thread: QualifiedThreadId,
6321 ) -> Result<Instance> {
6322 let waiting = &mut self.get_mut(set)?.waiting;
6323 let instance = match waiting.get(&thread) {
6324 Some(WaitMode::Callback(instance)) => *instance,
6325 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6326 };
6327 waiting.remove(&thread);
6328 Ok(instance)
6329 }
6330
6331 fn push<V: Send + Sync + 'static>(
6332 &mut self,
6333 value: V,
6334 ) -> Result<TableId<V>, ResourceTableError> {
6335 self.table.get_mut().push(value).map(TableId::from)
6336 }
6337
6338 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6339 self.table.get_mut().get_mut(&Resource::from(id))
6340 }
6341
6342 pub fn add_child<T: 'static, U: 'static>(
6343 &mut self,
6344 child: TableId<T>,
6345 parent: TableId<U>,
6346 ) -> Result<(), ResourceTableError> {
6347 self.table
6348 .get_mut()
6349 .add_child(Resource::from(child), Resource::from(parent))
6350 }
6351
6352 pub fn remove_child<T: 'static, U: 'static>(
6353 &mut self,
6354 child: TableId<T>,
6355 parent: TableId<U>,
6356 ) -> Result<(), ResourceTableError> {
6357 self.table
6358 .get_mut()
6359 .remove_child(Resource::from(child), Resource::from(parent))
6360 }
6361
6362 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6363 self.table.get_mut().delete(Resource::from(id))
6364 }
6365
6366 fn push_future(&mut self, future: HostTaskFuture) {
6367 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6374 }
6375
6376 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6377 if self.switch_item.is_some() {
6378 self.push_high_priority(item);
6381 bail_bug!("switch item already set");
6382 }
6383
6384 self.switch_item = Some(item);
6385 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6386
6387 Ok(())
6388 }
6389
6390 fn take_next_switch_item(&mut self) -> Result<()> {
6391 if let Some(item) = self.next_switch_item.take() {
6392 self.set_switch_item(item)?;
6393 }
6394 Ok(())
6395 }
6396
6397 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6400 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6403 return Ok(());
6404 };
6405 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6406 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6407 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6408 }
6409 let instance = self.get_mut(thread.task)?.instance;
6410 self.get_mut(thread.thread)?.wake_on_cancel.take();
6411 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6412
6413 let item = match mode {
6414 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6415 instance,
6416 thread,
6417 fiber,
6418 },
6419 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6420 instance,
6421 call: GuestCall {
6422 thread,
6423 kind: GuestCallKind::DeliverEvent {
6424 instance: callback_instance,
6425 set: Some(set),
6426 },
6427 },
6428 },
6429 };
6430 self.push_high_priority(item);
6431 Ok(())
6432 }
6433
6434 fn push_high_priority(&mut self, item: WorkItem) {
6435 self.high_priority.push_front(item);
6436 log::trace!(
6437 "push high priority: {:?}",
6438 self.high_priority.front().unwrap()
6439 );
6440 }
6441
6442 fn push_low_priority(&mut self, item: WorkItem) {
6443 self.low_priority.push_front(item);
6444 log::trace!(
6445 "push low priority: {:?}",
6446 self.low_priority.front().unwrap()
6447 );
6448 }
6449
6450 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6451 match priority {
6452 Priority::Switch => self.set_switch_item(item)?,
6453 Priority::High => self.push_high_priority(item),
6454 Priority::Low => self.push_low_priority(item),
6455 }
6456
6457 Ok(())
6458 }
6459
6460 fn promote_instance_local_thread_work_item(
6461 &mut self,
6462 current_instance: RuntimeInstance,
6463 ) -> Result<bool> {
6464 log::trace!("promote thread work items for {current_instance:?}");
6465
6466 self.promote_work_item_matching(|item: &WorkItem| {
6467 let result = match item {
6468 WorkItem::ResumeThread { instance, .. }
6469 | WorkItem::ResumeFiber { instance, .. }
6470 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6471 _ => false,
6472 };
6473
6474 log::trace!("candidate {item:?}: {result}");
6475 result
6476 })
6477 }
6478
6479 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6480 self.promote_work_item_matching(|item: &WorkItem| match item {
6481 WorkItem::ResumeThread {
6482 thread: item_thread,
6483 ..
6484 }
6485 | WorkItem::GuestCall {
6486 call:
6487 GuestCall {
6488 thread: item_thread,
6489 ..
6490 },
6491 ..
6492 } => *item_thread == thread,
6493 _ => false,
6494 })
6495 }
6496
6497 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6498 where
6499 F: FnMut(&WorkItem) -> bool,
6500 {
6501 if self.switch_item.is_none() {
6508 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6509 self.switch_item = self.high_priority.remove(index);
6510 }
6511 }
6512 if self.switch_item.is_none() {
6513 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6514 self.switch_item = self.low_priority.remove(index);
6515 }
6516 }
6517 Ok(self.switch_item.is_some())
6518 }
6519
6520 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6523 match task {
6524 Scope::HostId(task) => {
6525 let task: TableId<HostTask> = TableId::new(task);
6526 Ok(&mut self.get_mut(task)?.call_context)
6527 }
6528 Scope::Id(task) => {
6529 let task: TableId<GuestTask> = TableId::new(task);
6530 Ok(&mut self.get_mut(task)?.call_context)
6531 }
6532 }
6533 }
6534
6535 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6536 self.deferred_host_call_context.as_mut()
6537 }
6538
6539 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6540 match self.futures.get_mut().as_mut() {
6541 Some(f) => Ok(f),
6542 None => bail_bug!("futures field of concurrent state is currently taken"),
6543 }
6544 }
6545
6546 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6547 self.table.get_mut()
6548 }
6549
6550 fn debug_assert_deferred_host_invariant(&self) {
6551 debug_assert_eq!(
6552 self.deferred_host_call_context.is_some(),
6553 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6554 "a deferred host thread and call context must exist together",
6555 );
6556 }
6557
6558 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6559 self.debug_assert_deferred_host_invariant();
6560 let caller = match self.unforced_current_thread {
6561 CurrentThread::DeferredHost(caller) => caller,
6562 thread => return Ok(thread),
6563 };
6564
6565 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6567 let task = self.push(task)?;
6568 let call_context = self
6569 .deferred_host_call_context
6570 .take()
6571 .expect("deferred host call context should be present");
6572 self.get_mut(task)
6573 .expect("newly inserted host task should be present")
6574 .call_context = call_context;
6575 self.unforced_current_thread = CurrentThread::Host(task);
6576 self.debug_assert_deferred_host_invariant();
6577 log::trace!("new host task materialized {task:?}");
6578 Ok(CurrentThread::Host(task))
6579 }
6580
6581 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6582 match self.materialize_host_task()? {
6583 CurrentThread::Host(id) => Ok(Some(id)),
6584 CurrentThread::None => Ok(None),
6585 CurrentThread::Guest(_) => {
6586 bail_bug!("tried to materialize a host task id from a guest thread")
6587 }
6588 CurrentThread::DeferredHost(_) => {
6589 bail_bug!(
6590 "current thread is a deferred host thread which should have been materialized"
6591 )
6592 }
6593 }
6594 }
6595
6596 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6597 match self.materialize_host_task()? {
6598 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6599 _ => bail_bug!("current scope is not a deferred host scope"),
6600 }
6601 }
6602
6603 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6604 let count = &mut self.get_mut(task)?.ref_count;
6605 *count += 1;
6606 log::trace!("increment {task:?} to {count}");
6607 Ok(())
6608 }
6609
6610 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6611 let count = &mut self.get_mut(task)?.ref_count;
6612 if *count == 0 {
6613 bail_bug!("can't decrement task ref count that's already zero");
6614 }
6615 *count -= 1;
6616 log::trace!("decrement {task:?} to {count}");
6617 if *count == 0 {
6618 let task = self.delete(task)?;
6619
6620 self.decrement_group_ref_count(task.group)?;
6621
6622 debug_assert!(task.decremented_interesting_task_count);
6629 }
6630 Ok(())
6631 }
6632
6633 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6634 log::trace!("delete host task {task:?}");
6635 let task = self.delete(task)?;
6636
6637 self.decrement_group_ref_count(task.group)
6638 }
6639}
6640
6641fn for_any_lower<
6644 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6645>(
6646 fun: F,
6647) -> F {
6648 fun
6649}
6650
6651fn for_any_lift<
6653 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6654>(
6655 fun: F,
6656) -> F {
6657 fun
6658}
6659
6660fn check_ambient_store(id: StoreId) {
6661 let message = "\
6662 `Future`s which depend on asynchronous component tasks, streams, or \
6663 futures to complete may only be polled from the event loop of the \
6664 store to which they belong. Please use \
6665 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6666 ";
6667 tls::try_get(|store| {
6668 let matched = match store {
6669 tls::TryGet::Some(store) => store.id() == id,
6670 tls::TryGet::Taken | tls::TryGet::None => false,
6671 };
6672
6673 if !matched {
6674 panic!("{message}")
6675 }
6676 });
6677}
6678
6679fn unpack_callback_code(code: u32) -> (u32, u32) {
6680 (code & 0xF, code >> 4)
6681}
6682
6683struct WaitableCheckParams {
6687 set: TableId<WaitableSet>,
6688 options: OptionsIndex,
6689 payload: u32,
6690}
6691
6692enum WaitableCheck {
6695 Wait,
6696 Poll,
6697}
6698
6699pub(crate) struct PreparedCall<R> {
6701 handle: Func,
6703 thread: QualifiedThreadId,
6705 param_count: usize,
6707 rx: oneshot::Receiver<LiftedResult>,
6710 _phantom: PhantomData<R>,
6711}
6712
6713impl<R> PreparedCall<R> {
6714 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6721 store
6722 .concurrent_state_mut()?
6723 .increment_task_ref_count(self.thread.task)?;
6724
6725 Ok(TaskId {
6726 task: self.thread.task,
6727 })
6728 }
6729}
6730
6731pub(crate) struct TaskId {
6733 task: TableId<GuestTask>,
6734}
6735
6736impl TaskId {
6737 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6749 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6750 if !task.already_lowered_parameters() {
6751 task.lower_params = Some(Box::new(|store, _| {
6756 store.set_trapped();
6757 bail!("the host future for this call was dropped before it started")
6758 }));
6759 } else {
6760 _ = store
6765 .concurrent_state_mut()?
6766 .decrement_task_ref_count(self.task);
6767 }
6768
6769 Ok(())
6770 }
6771}
6772
6773pub(crate) fn prepare_call<T, R>(
6779 mut store: StoreContextMut<T>,
6780 handle: Func,
6781 param_count: usize,
6782 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6783 + Send
6784 + Sync
6785 + 'static,
6786 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6787 + Send
6788 + Sync
6789 + 'static,
6790) -> Result<PreparedCall<R>> {
6791 if !store.0.may_enter() {
6792 bail!(Trap::CannotEnterComponent);
6793 }
6794
6795 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6796
6797 let instance = handle.instance().id().get(store.0);
6798 let options = &instance.component().env_component().options[options];
6799 let ty = &instance.component().types()[ty];
6800 let async_typed = ty.async_;
6801 let async_lifted = raw_options.async_;
6802 let task_return_type = ty.results;
6803 let component_instance = raw_options.instance;
6804 let callback = options.callback.map(|i| instance.runtime_callback(i));
6805 let memory = options
6806 .memory()
6807 .map(|i| instance.runtime_memory(i))
6808 .map(SendSyncPtr::new);
6809 let string_encoding = options.string_encoding;
6810 let token = StoreToken::new(store.as_context_mut());
6811 let caller = store.0.materialize_host_task_id()?;
6812 let state = store.0.concurrent_state_mut()?;
6813
6814 let (tx, rx) = oneshot::channel();
6815
6816 let instance = handle.instance().runtime_instance(component_instance);
6817 let thread = GuestTask::new(
6818 state,
6819 Box::new(for_any_lower(move |store, params| {
6820 lower_params(token.as_context_mut(store), params)
6821 })),
6822 LiftResult {
6823 lift: Box::new(for_any_lift(move |store, result| {
6824 lift_result(store, result)
6825 })),
6826 ty: task_return_type,
6827 memory,
6828 string_encoding,
6829 },
6830 Caller::Host {
6831 tx: Some(tx),
6832 caller,
6833 },
6834 callback.map(|callback| {
6835 let callback = SendSyncPtr::new(callback);
6836 let instance = handle.instance();
6837 Box::new(move |store: &mut dyn VMStore, event, handle| {
6838 let store = token.as_context_mut(store);
6839 unsafe { instance.call_callback(store, callback, event, handle) }
6842 }) as CallbackFn
6843 }),
6844 instance,
6845 async_typed,
6846 async_lifted,
6847 )?;
6848
6849 Ok(PreparedCall {
6850 handle,
6851 thread,
6852 param_count,
6853 rx,
6854 _phantom: PhantomData,
6855 })
6856}
6857
6858pub(crate) struct StagedCall<R> {
6859 store: StoreId,
6860 rx: oneshot::Receiver<LiftedResult>,
6861 _marker: PhantomData<fn() -> R>,
6862 group: TaskGroupId,
6870}
6871
6872impl<R> StagedCall<R> {
6873 pub(crate) fn new<T: 'static>(
6884 mut store: StoreContextMut<T>,
6885 prepared: PreparedCall<R>,
6886 ) -> Result<StagedCall<R>> {
6887 let PreparedCall {
6888 handle,
6889 thread,
6890 param_count,
6891 rx,
6892 ..
6893 } = prepared;
6894
6895 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6896
6897 let state = store.0.concurrent_state_mut()?;
6898 let group = state.get_mut(thread.task)?.group;
6899 state.decrement_task_ref_count(thread.task)?;
6900
6901 Ok(StagedCall {
6902 store: store.0.id(),
6903 rx,
6904 _marker: PhantomData,
6905 group,
6906 })
6907 }
6908}
6909
6910impl<R> Future for StagedCall<R>
6911where
6912 R: 'static,
6913{
6914 type Output = Result<R>;
6915
6916 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6917 check_ambient_store(self.store);
6918 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6919 Ok(r) => match r.downcast() {
6920 Ok(r) => Ok(*r),
6921 Err(_) => bail_bug!("wrong type of value produced"),
6922 },
6923 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6924 })
6925 }
6926}
6927
6928fn stage_call0<T: 'static>(
6931 store: StoreContextMut<T>,
6932 handle: Func,
6933 guest_thread: QualifiedThreadId,
6934 param_count: usize,
6935) -> Result<()> {
6936 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6937 let is_concurrent = raw_options.async_;
6938 let callback = raw_options.callback;
6939 let instance = handle.instance();
6940 let callee = handle.lifted_core_func(store.0);
6941 let post_return = raw_options
6942 .post_return
6943 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6944 let callback = callback.map(|i| {
6945 let instance = instance.id().get(store.0);
6946 SendSyncPtr::new(instance.runtime_callback(i))
6947 });
6948
6949 log::trace!("queueing call {guest_thread:?}");
6950
6951 unsafe {
6955 instance.stage_call(
6956 store,
6957 guest_thread,
6958 SendSyncPtr::new(callee),
6959 param_count,
6960 1,
6961 is_concurrent,
6962 callback,
6963 post_return.map(SendSyncPtr::new),
6964 true,
6965 )
6966 }
6967}
6968
6969#[cfg(all(test, feature = "cranelift", feature = "wat"))]
6970mod tests {
6971 use super::*;
6972 use crate::component::{Component, Linker};
6973 use crate::store::AsStoreOpaque;
6974 use crate::{Config, Engine};
6975
6976 fn host_subtask(
6977 state: HostTaskState,
6978 event: Option<Event>,
6979 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
6980 let mut config = Config::new();
6981 config.wasm_component_model_async(true);
6982 let engine = Engine::new(&config)?;
6983 let component = Component::new(&engine, "(component)")?;
6984 let mut store = Store::new(&engine, ());
6985 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
6986 let store_opaque = store.as_store_opaque();
6987 let concurrent_state = store_opaque.concurrent_state_mut()?;
6988 let group = concurrent_state.make_task_group()?;
6991 let task = concurrent_state.push(HostTask {
6992 common: WaitableCommon::default(),
6993 call_context: CallContext::default(),
6994 state,
6995 group,
6996 })?;
6997 let (state, counter) = store_opaque.instance_state_and_handle_counter(
6998 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
6999 );
7000 let handle = state
7001 .handle_table()
7002 .subtask_insert_host(counter, task.rep())?;
7003 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7004 common.handle = Some(handle);
7005 common.event = event;
7006 Ok((store, instance, task, handle))
7007 }
7008
7009 #[test]
7010 fn host_subtask_drop_during_cancellation() -> Result<()> {
7011 for abort_completed in [false, true] {
7012 let (handle, future) = JoinHandle::run(future::pending::<()>());
7013 let mut future = pin!(future);
7014 let (mut store, instance, task, handle) =
7015 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7016 let store = store.as_store_opaque();
7017 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7018 assert_eq!(
7019 instance.subtask_cancel(store, caller, true, handle)?,
7020 BLOCKED
7021 );
7022 if abort_completed {
7023 assert!(matches!(
7026 future
7027 .as_mut()
7028 .poll(&mut Context::from_waker(Waker::noop())),
7029 Poll::Ready(None),
7030 ));
7031 }
7032 for async_ in [false, true] {
7033 let err = instance
7034 .subtask_cancel(store, caller, async_, handle)
7035 .unwrap_err();
7036 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7037 }
7038 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7039 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7040 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7041 }
7042 Ok(())
7043 }
7044
7045 #[test]
7046 fn host_subtask_cancel_after_completion() -> Result<()> {
7047 for async_ in [false, true] {
7048 let (mut store, instance, task, handle) = host_subtask(
7049 HostTaskState::CalleeDone { cancelled: false },
7050 Some(Event::Subtask {
7051 status: Status::Returned,
7052 }),
7053 )?;
7054 let store = store.as_store_opaque();
7055 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7056 assert_eq!(
7057 instance.subtask_cancel(store, caller, async_, handle)?,
7058 Status::Returned as u32,
7059 );
7060 let err = instance
7061 .subtask_cancel(store, caller, async_, handle)
7062 .unwrap_err();
7063 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7064 instance.subtask_drop(store, caller, handle)?;
7065 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7066 }
7067 Ok(())
7068 }
7069
7070 #[test]
7071 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7072 for (cancelled, status) in [
7073 (false, Status::Returned),
7074 (true, Status::Returned),
7075 (true, Status::ReturnCancelled),
7076 ] {
7077 for delivered in [false, true] {
7078 let event = if delivered {
7079 None
7080 } else {
7081 Some(Event::Subtask { status })
7082 };
7083 let (mut store, instance, task, handle) =
7084 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7085 let store = store.as_store_opaque();
7086 let result = instance.subtask_drop(
7087 store,
7088 RuntimeComponentInstanceIndex::from_u32(0),
7089 handle,
7090 );
7091 if delivered {
7092 result?;
7093 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7094 } else {
7095 let err = result.unwrap_err();
7096 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7097 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7098 }
7099 }
7100 }
7101 Ok(())
7102 }
7103}