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
1159 .concurrent_state_mut()
1160 .unwrap()
1161 .table
1162 .get_mut()
1163 .iter_mut()
1164 .count()
1165 }
1166
1167 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1177 where
1178 T: 'static,
1179 {
1180 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1181 self.spawn_with_accessor(accessor, task)
1182 }
1183
1184 fn spawn_with_accessor<D>(
1187 self,
1188 accessor: Accessor<T, D>,
1189 task: impl for<'fut> AccessorTask<'fut, T, D>,
1190 ) -> Result<JoinHandle>
1191 where
1192 T: 'static,
1193 D: HasData + ?Sized,
1194 {
1195 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1199 self.0
1200 .concurrent_state_mut()?
1201 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1202 Ok(handle)
1203 }
1204
1205 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1289 where
1290 T: Send + 'static,
1291 {
1292 ensure!(
1293 self.0.concurrency_support(),
1294 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1295 );
1296 self.do_run_concurrent(fun, false).await
1297 }
1298
1299 pub(super) async fn run_concurrent_trap_on_idle<R>(
1300 self,
1301 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1302 ) -> Result<R> {
1303 self.do_run_concurrent(fun, true).await
1304 }
1305
1306 async fn do_run_concurrent<R>(
1307 mut self,
1308 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1309 trap_on_idle: bool,
1310 ) -> Result<R> {
1311 debug_assert!(self.0.concurrency_support());
1312 let already_running = self
1313 .0
1314 .concurrent_state_mut_already_forced_current_thread()
1315 .event_loop_running;
1316 if already_running {
1317 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1318 }
1319 let token = StoreToken::new(self.as_context_mut());
1320
1321 struct Dropper<'a, T: 'static, V> {
1322 store: StoreContextMut<'a, T>,
1323 value: ManuallyDrop<V>,
1324 }
1325
1326 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1327 fn drop(&mut self) {
1328 self.store
1329 .0
1330 .concurrent_state_mut_already_forced_current_thread()
1331 .event_loop_running = false;
1332
1333 tls::set(self.store.0, || {
1334 unsafe { ManuallyDrop::drop(&mut self.value) }
1339 });
1340 }
1341 }
1342
1343 let accessor = &Accessor::new(token);
1344 self.0
1345 .concurrent_state_mut_already_forced_current_thread()
1346 .event_loop_running = true;
1347 let dropper = &mut Dropper {
1348 store: self,
1349 value: ManuallyDrop::new(fun(accessor)),
1350 };
1351 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1353
1354 let result = dropper
1355 .store
1356 .as_context_mut()
1357 .poll_until(future, trap_on_idle)
1358 .await;
1359
1360 if result.is_err() {
1361 dropper.store.0.set_trapped();
1362 }
1363
1364 result
1365 }
1366
1367 async fn poll_until<R>(
1373 mut self,
1374 mut future: Pin<&mut impl Future<Output = R>>,
1375 trap_on_idle: bool,
1376 ) -> Result<R> {
1377 struct Reset<'a, T: 'static> {
1378 store: StoreContextMut<'a, T>,
1379 futures: Option<FuturesUnordered<HostTaskFuture>>,
1380 }
1381
1382 impl<'a, T> Drop for Reset<'a, T> {
1383 fn drop(&mut self) {
1384 if let Some(futures) = self.futures.take() {
1385 *self
1386 .store
1387 .0
1388 .concurrent_state_mut_already_forced_current_thread()
1389 .futures
1390 .get_mut() = Some(futures);
1391 }
1392 }
1393 }
1394
1395 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1399 let mut turns_without_yield = 0;
1400
1401 loop {
1402 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1406 let mut reset = Reset {
1407 store: self.as_context_mut(),
1408 futures,
1409 };
1410 let mut next = match reset.futures.as_mut() {
1411 Some(f) => pin!(f.next()),
1412 None => bail_bug!("concurrent state missing futures field"),
1413 };
1414
1415 enum PollResult<R> {
1416 Complete(R),
1417 ProcessWork {
1418 ready: Option<WorkItem>,
1419 low_priority: bool,
1420 },
1421 }
1422
1423 let result = future::poll_fn(|cx| {
1424 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1427 return Poll::Ready(Ok(PollResult::Complete(value)));
1428 }
1429
1430 if reset.store.0.trapped() {
1439 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1440 }
1441
1442 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1446 Poll::Ready(Some(output)) => {
1447 match output {
1448 Err(e) => return Poll::Ready(Err(e)),
1449 Ok(()) => {}
1450 }
1451 Poll::Ready(true)
1452 }
1453 Poll::Ready(None) => Poll::Ready(false),
1454 Poll::Pending => Poll::Pending,
1455 };
1456
1457 let state = reset.store.0.concurrent_state_mut()?;
1472 let mut ready = state.switch_item.take();
1473 let mut low_priority = false;
1474 if ready.is_none() {
1475 ready = state.high_priority.pop_back();
1476 if ready.is_none() {
1477 ready = state.low_priority.pop_back();
1478 low_priority = true;
1479 }
1480 }
1481 if ready.is_some() {
1482 return Poll::Ready(Ok(PollResult::ProcessWork {
1483 ready,
1484 low_priority,
1485 }));
1486 }
1487
1488 return match next {
1492 Poll::Ready(true) => {
1493 Poll::Ready(Ok(PollResult::ProcessWork {
1499 ready: None,
1500 low_priority: false,
1501 }))
1502 }
1503 Poll::Ready(false) => {
1504 if let Poll::Ready(value) =
1508 tls::set(reset.store.0, || future.as_mut().poll(cx))
1509 {
1510 Poll::Ready(Ok(PollResult::Complete(value)))
1511 } else {
1512 if trap_on_idle {
1518 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1525 Trap::CannotBlockSyncTask.into()
1526 } else {
1527 Trap::AsyncDeadlock.into()
1529 }))
1530 } else {
1531 Poll::Pending
1535 }
1536 }
1537 }
1538 Poll::Pending => Poll::Pending,
1543 };
1544 })
1545 .await;
1546
1547 drop(reset);
1551
1552 match result? {
1553 PollResult::Complete(value) => break Ok(value),
1556 PollResult::ProcessWork {
1559 mut ready,
1560 low_priority,
1561 } => {
1562 if low_priority {
1590 if let Some(item) = ready.take() {
1591 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1592 }
1593 self.0.yield_now().await;
1594 turns_without_yield = 0;
1595 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1596 }
1597
1598 if let Some(item) = ready {
1599 self.as_context_mut().handle_work_item(item).await?;
1600 }
1601
1602 turns_without_yield += 1;
1603 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1604 turns_without_yield = 0;
1605 self.0.yield_now().await;
1606 }
1607 }
1608 }
1609 }
1610 }
1611
1612 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1614 let item = match item {
1618 WorkItem::ResumeFiber {
1619 instance,
1620 thread,
1621 fiber,
1622 } => {
1623 let future = self.0.resume_fiber(fiber);
1624 log::trace!(
1625 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1626 );
1627 return futures::future::Either::Left(future);
1628 }
1629 item => item,
1630 };
1631 futures::future::Either::Right(async move {
1632 log::trace!("handle work item {item:?}");
1633 match item {
1634 WorkItem::PushFuture(future) => {
1635 self.0
1636 .concurrent_state_mut()?
1637 .futures_mut()?
1638 .push(future.into_inner());
1639 }
1640 WorkItem::ResumeFiber { .. } => unreachable!(),
1641 WorkItem::ResumeThread { thread, .. } => {
1642 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1643 if !matches!(slot, GuestThreadState::Ready { .. }) {
1644 bail_bug!("cannot resume non-pending thread {thread:?}");
1645 }
1646 let GuestThreadState::Ready { fiber } =
1647 mem::replace(slot, GuestThreadState::Running)
1648 else {
1649 unreachable!()
1650 };
1651 self.0.resume_fiber(fiber).await?;
1652 }
1653 WorkItem::GuestCall { call, .. } => {
1654 if call.is_ready(self.0)? {
1655 self.0
1656 .concurrent_state_mut()?
1657 .get_mut(call.thread.thread)?
1658 .wake_on_cancel = WakeOnCancel::None;
1659 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1660 } else {
1661 let state = self.0.concurrent_state_mut()?;
1662 let task = state.get_mut(call.thread.task)?;
1663 if !task.starting_sent {
1664 task.starting_sent = true;
1665 if let GuestCallKind::StartImplicit(_) = &call.kind {
1666 Waitable::Guest(call.thread.task).set_event(
1667 state,
1668 Some(Event::Subtask {
1669 status: Status::Starting,
1670 }),
1671 )?;
1672 }
1673 }
1674
1675 let instance = state.get_mut(call.thread.task)?.instance;
1676 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1685 let state = self.0.concurrent_state_mut()?;
1686 if !state.get_mut(*set)?.ready.is_empty() {
1687 state.wake_waiter(*set)?;
1688 }
1689 }
1690 self.0
1691 .instance_state(instance)
1692 .concurrent_state()
1693 .pending
1694 .insert(call.thread, call.kind);
1695
1696 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1700 }
1701 }
1702 WorkItem::WorkerFunction(fun) => {
1703 self.run_on_worker(WorkerItem::Function(fun)).await?;
1704 }
1705 }
1706
1707 Ok(())
1708 })
1709 }
1710
1711 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1713 let state = self.0.concurrent_state_mut()?;
1714 if state.worker_item.is_some() {
1715 bail_bug!("worker item already set when taking worker fiber");
1716 }
1717 let worker = if let Some(fiber) = state.worker.take() {
1718 fiber
1719 } else {
1720 unsafe {
1739 fiber::make_fiber_unchecked(self.0, move |store| {
1740 loop {
1741 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1742 bail_bug!("worker_item not present when resuming fiber")
1743 };
1744 match item {
1745 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1746 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1747 }
1748
1749 store.suspend(SuspendReason::NeedWork)?;
1750 }
1751 })?
1752 }
1753 };
1754
1755 self.0
1758 .concurrent_state_mut_already_forced_current_thread()
1759 .worker_item = Some(item);
1760
1761 self.0.resume_fiber(worker).await
1762 }
1763
1764 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1769 where
1770 T: 'static,
1771 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1772 + Send
1773 + Sync
1774 + 'static,
1775 R: Send + Sync + 'static,
1776 {
1777 let token = StoreToken::new(self);
1778 async move {
1779 let mut accessor = Accessor::new(token);
1780 closure(&mut accessor).await
1781 }
1782 }
1783
1784 pub(crate) async fn start_instance(
1785 &mut self,
1786 instance: ModuleInstance,
1787 callee: Option<RuntimeInstance>,
1788 ) -> Result<ModuleInstance> {
1789 let (tx, rx) = oneshot::channel();
1790 let token = StoreToken::new(self.as_context_mut());
1791 self.0.queue_task(move |store| {
1792 _ = tx.send(
1793 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1794 .map(|()| instance),
1795 );
1796 Ok(())
1797 })?;
1798 self.as_context_mut()
1799 .run_concurrent_trap_on_idle(async |_| {
1800 rx.await
1801 .map_err(|_| format_err!("oneshot channel canceled"))
1802 })
1803 .await??
1804 }
1805}
1806
1807pub type EnteredHostTask = Option<QualifiedThreadId>;
1814
1815impl StoreOpaque {
1816 #[inline]
1820 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1821 if !self.concurrency_support() {
1823 return Ok(CurrentThread::None);
1824 }
1825
1826 if !self
1829 .vm_store_context_mut()
1830 .current_thread_mut()
1831 .is_deferred()
1832 {
1833 return Ok(self
1834 .concurrent_state_mut_already_forced_current_thread()
1835 .unforced_current_thread);
1836 }
1837
1838 self.force_deferred_current_thread()
1839 }
1840
1841 #[cold]
1844 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1845 let state = self.concurrent_state_mut_without_forcing_current_thread();
1854 let id = match state.unforced_current_thread.guest_task() {
1855 Some(task) => state.get_mut(task)?.instance.instance,
1856 None => bail_bug!("deferred component-model thread with non-guest base"),
1857 };
1858
1859 let mut frames = Vec::new();
1862 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1863 while let Some(ptr) = cur.as_deferred() {
1864 let deferred = unsafe { ptr.as_non_null().as_ref() };
1869 frames.push((
1870 deferred.callee_async != 0,
1871 deferred.callee_instance,
1872 deferred.saved_context,
1873 ));
1874 cur = deferred.parent;
1875 }
1876
1877 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1881
1882 let current_context = *self.vm_store_context_mut().component_context_mut();
1885
1886 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1890 *self.vm_store_context_mut().component_context_mut() = saved_context;
1894 let callee = RuntimeInstance {
1895 instance: id,
1896 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1897 };
1898 self.enter_guest_sync_call(callee_async, callee)?;
1899 }
1900
1901 *self.vm_store_context_mut().component_context_mut() = current_context;
1903
1904 Ok(self
1905 .concurrent_state_mut_without_forcing_current_thread()
1906 .unforced_current_thread)
1907 }
1908
1909 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1910 match self.current_thread()?.guest() {
1911 Some(id) => Ok(*id),
1912 None => bail_bug!("current thread is not a guest thread"),
1913 }
1914 }
1915
1916 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1920 match self.current_thread()? {
1921 CurrentThread::Host(id) => Ok(Some(id)),
1922 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1923 _ => bail_bug!("current thread is not a host thread"),
1924 }
1925 }
1926
1927 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1930 Ok(self
1931 .concurrent_state_mut()?
1932 .materialize_current_host_task_id()?)
1933 }
1934
1935 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1936 log::trace!("enter sync-typed call {callee:?}");
1937 let state = self.instance_state(callee).concurrent_state();
1938 let old_do_not_suspend = state.do_not_suspend;
1939 state.do_not_suspend = true;
1940
1941 let thread = self.current_guest_thread()?;
1942 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1943 if thread.old_do_not_suspend.is_some() {
1944 bail_bug!("current thread already has `old_do_not_suspend` value");
1945 }
1946
1947 thread.old_do_not_suspend = Some(old_do_not_suspend);
1948
1949 Ok(())
1950 }
1951
1952 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1953 log::trace!("exit sync-typed call {callee:?}");
1954 let thread = self.current_guest_thread()?;
1955 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1956 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1957 bail_bug!("current thread missing `old_do_not_suspend` value");
1958 };
1959 let state = self.instance_state(callee).concurrent_state();
1960 state.do_not_suspend = old_do_not_suspend;
1961 Ok(())
1962 }
1963
1964 pub(crate) fn enter_guest_sync_call(
1976 &mut self,
1977 callee_async_typed: bool,
1978 callee: RuntimeInstance,
1979 ) -> Result<()> {
1980 log::trace!("enter sync-lifted call {callee:?}");
1981 if !self.concurrency_support() {
1982 return self.enter_call_not_concurrent();
1983 }
1984
1985 let state = self.concurrent_state_mut()?;
1989 let item = state.next_switch_item.take();
1990 state.saved_next_switch_items.push(item);
1991
1992 let thread = self.current_thread()?;
1993 let caller = if let Some(thread) = thread.guest() {
1994 Caller::Guest { thread: *thread }
1995 } else {
1996 Caller::Host {
1997 tx: None,
1998 caller: self.materialize_host_task_id()?,
1999 }
2000 };
2001
2002 let state = self.concurrent_state_mut()?;
2003 let guest_thread = GuestTask::new(
2004 state,
2005 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2006 LiftResult {
2007 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2008 ty: TypeTupleIndex::reserved_value(),
2009 memory: None,
2010 string_encoding: StringEncoding::Utf8,
2011 },
2012 caller,
2013 None,
2014 callee,
2015 callee_async_typed,
2016 false,
2017 )?;
2018
2019 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2020 guest_thread.thread,
2021 self,
2022 callee.index,
2023 )?;
2024 self.set_thread(guest_thread)?;
2025
2026 if !callee_async_typed {
2027 self.enter_sync_call(callee)?;
2028 }
2029
2030 Ok(())
2031 }
2032
2033 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2041 if !self.concurrency_support() {
2042 return Ok(self.exit_call_not_concurrent());
2043 }
2044
2045 let thread = match self.current_thread()?.guest() {
2046 Some(t) => *t,
2047 None => bail_bug!("expected task when exiting"),
2048 };
2049 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2050 let instance = task.instance;
2051
2052 let caller = match &task.caller {
2053 &Caller::Guest { thread } => thread.into(),
2054 &Caller::Host { caller, .. } => caller
2055 .map(CurrentThread::Host)
2056 .unwrap_or(CurrentThread::None),
2057 };
2058 task.lift_result = None;
2059 task.exited = true;
2060 let async_typed = task.async_typed;
2061
2062 if !async_typed {
2063 self.exit_sync_call(instance)?;
2064 }
2065
2066 self.set_thread(caller)?;
2067
2068 log::trace!("exit sync-lifted call {instance:?}");
2069
2070 if async_typed {
2071 self.switch_or_trap_if_may_not_suspend(instance)?;
2076 }
2077
2078 self.cleanup_thread(thread, instance)?;
2079
2080 self.concurrent_state_mut()?
2081 .decrement_task_ref_count(thread.task)?;
2082
2083 let state = self.concurrent_state_mut()?;
2084 let Some(item) = state.saved_next_switch_items.pop() else {
2085 bail_bug!("unable to pop from `saved_next_switch_items`");
2086 };
2087 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2088 state.push_high_priority(item);
2091 bail_bug!("`next_switch_item` unexpectedly already set");
2092 }
2093
2094 Ok(())
2095 }
2096
2097 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2104 if !self.concurrency_support() {
2105 self.enter_call_not_concurrent()?;
2106 return Ok(None);
2107 }
2108 let caller = self.current_guest_thread()?;
2109 log::trace!("new deferred host task with caller {caller:?}");
2110
2111 self.set_thread(CurrentThread::DeferredHost(caller))?;
2112 let state = self.concurrent_state_mut()?;
2113 debug_assert!(state.deferred_host_call_context.is_none());
2114 state.deferred_host_call_context = Some(CallContext::default());
2115 state.debug_assert_deferred_host_invariant();
2116 Ok(Some(caller))
2117 }
2118
2119 pub(crate) fn host_task_delete(
2126 &mut self,
2127 original_task: EnteredHostTask,
2128 materialized_task: Option<TableId<HostTask>>,
2129 ) -> Result<()> {
2130 match original_task {
2131 Some(caller) => {
2132 self.set_thread(caller)?;
2133 if materialized_task.is_none() {
2134 let state = self.concurrent_state_mut()?;
2135 let context = state
2136 .deferred_host_call_context
2137 .take()
2138 .expect("deferred host call context should be present");
2139 debug_assert!(context.is_empty());
2140 state.debug_assert_deferred_host_invariant();
2141 }
2142 log::trace!(
2143 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2144 );
2145 if let Some(task) = materialized_task {
2146 self.concurrent_state_mut()?.delete_host_task(task)?;
2147 }
2148 }
2149 None => {
2150 debug_assert!(materialized_task.is_none());
2151 self.exit_call_not_concurrent();
2152 }
2153 }
2154 Ok(())
2155 }
2156
2157 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2160 self.component_instance_mut(instance.instance)
2161 .instance_state(instance.index)
2162 }
2163
2164 fn instance_state_and_handle_counter(
2167 &mut self,
2168 instance: RuntimeInstance,
2169 ) -> (&mut InstanceState, &mut HandleCounter) {
2170 let (component, counter) = self
2171 .store_data_mut()
2172 .component_instance_and_handle_counter_mut(instance.instance);
2173 (component.instance_state(instance.index), counter)
2174 }
2175
2176 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2182 let thread = thread.into();
2183 let state = self.concurrent_state_mut()?;
2184 state.debug_assert_deferred_host_invariant();
2185 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2186
2187 state.handle_thread_switch(old_thread, thread)?;
2188
2189 if let Some(old_thread) = old_thread.guest() {
2197 let old_context = *self.vm_store_context_mut().component_context_mut();
2198 self.concurrent_state_mut()?
2199 .get_mut(old_thread.thread)?
2200 .context = old_context;
2201 }
2202 if cfg!(debug_assertions) {
2203 *self.vm_store_context_mut().component_context_mut() =
2204 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2205 }
2206 if let Some(thread) = thread.guest() {
2207 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2208 let context = thread.context;
2209 if cfg!(debug_assertions) {
2210 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2211 }
2212 *self.vm_store_context_mut().component_context_mut() = context;
2213 }
2214
2215 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2217 VMLazyThread::none()
2218 } else {
2219 VMLazyThread::forced()
2220 };
2221
2222 Ok(old_thread)
2223 }
2224
2225 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2227 if self.switch_if_may_not_suspend(instance)? {
2228 Ok(())
2229 } else {
2230 Err(Trap::CannotBlockSyncTask.into())
2231 }
2232 }
2233
2234 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2238 self.concurrent_state_mut()?;
2242
2243 Ok(!self.concurrency_support()
2244 || !self
2245 .instance_state(instance)
2246 .concurrent_state()
2247 .do_not_suspend
2248 || self
2249 .concurrent_state_mut()?
2250 .promote_instance_local_thread_work_item(instance)?)
2251 }
2252
2253 fn enter_instance(&mut self, instance: RuntimeInstance) {
2257 log::trace!("enter {instance:?}");
2258 self.instance_state(instance)
2259 .concurrent_state()
2260 .do_not_enter = true;
2261 }
2262
2263 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2267 log::trace!("exit {instance:?}");
2268 self.instance_state(instance)
2269 .concurrent_state()
2270 .do_not_enter = false;
2271 self.partition_pending(instance)
2272 }
2273
2274 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2282 for (thread, kind) in
2283 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2284 {
2285 let call = GuestCall { thread, kind };
2286 if call.is_ready(self)? {
2287 self.concurrent_state_mut()?
2288 .push_high_priority(WorkItem::GuestCall { instance, call });
2289 } else {
2290 self.instance_state(instance)
2291 .concurrent_state()
2292 .pending
2293 .insert(call.thread, call.kind);
2294 }
2295 }
2296
2297 if let Some(waker) = self
2298 .concurrent_state_mut()?
2299 .ready_for_concurrent_call_waker
2300 .take()
2301 {
2302 waker.wake();
2303 }
2304
2305 Ok(())
2306 }
2307
2308 pub(crate) fn backpressure_modify(
2310 &mut self,
2311 caller_instance: RuntimeInstance,
2312 modify: impl FnOnce(u16) -> Option<u16>,
2313 ) -> Result<()> {
2314 let state = self.instance_state(caller_instance).concurrent_state();
2315 let old = state.backpressure;
2316 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2317 state.backpressure = new;
2318
2319 if old > 0 && new == 0 {
2320 self.partition_pending(caller_instance)?;
2323 }
2324
2325 Ok(())
2326 }
2327
2328 fn resume_fiber(
2331 &mut self,
2332 fiber: StoreFiber<'static>,
2333 ) -> impl Future<Output = Result<()>> + '_ {
2334 let mut resuming = ResumingFiber {
2335 store: self,
2336 fiber: Some(fiber),
2337 };
2338 async move {
2339 let old_thread = resuming.store.current_thread()?;
2340 log::trace!("resume_fiber: save current thread {old_thread:?}");
2341
2342 let fiber = resuming.fiber.take().unwrap();
2343 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2344
2345 resuming.store.set_thread(old_thread)?;
2346
2347 let state = resuming.store.concurrent_state_mut()?;
2348
2349 if let Some(ot) = old_thread.guest() {
2350 state.set_thread_running(ot.thread)?;
2351 }
2352 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2353
2354 if resuming.fiber.is_some() {
2355 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2356 let reason = match state.suspend_reason.take() {
2358 Some(r) => r,
2359 None => bail_bug!("suspend reason missing when resuming fiber"),
2360 };
2361 match reason {
2362 SuspendReason::NeedWork => {
2363 if state.worker.is_none() {
2364 state.worker = resuming.fiber.take();
2365 }
2366 }
2367 SuspendReason::Yielding { thread } => {
2368 let slot = &mut state.get_mut(thread.thread)?.state;
2369 slot.check_no_fiber()?;
2370 *slot = GuestThreadState::Ready {
2371 fiber: resuming.fiber.take().unwrap(),
2372 };
2373 let instance = state.get_mut(thread.task)?.instance;
2374 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2375 }
2376 SuspendReason::ExplicitlySuspending { thread } => {
2377 let slot = &mut state.get_mut(thread.thread)?.state;
2378 slot.check_no_fiber()?;
2379 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2380 }
2381 SuspendReason::Waiting { set, thread } => {
2382 match state.get_mut(set)?.waiting.entry(thread) {
2383 Entry::Vacant(entry) => {
2384 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2385 }
2386 Entry::Occupied(_) => {
2387 bail_bug!("entry unexpectedly already exists for {thread:?}");
2388 }
2389 }
2390 }
2391 SuspendReason::YieldingToSubtask { thread } => {
2392 let instance = state.get_mut(thread.task)?.instance;
2401 if state.next_switch_item.is_some() {
2402 bail_bug!(
2405 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2406 a thread wanted to wait on a subtask"
2407 );
2408 }
2409 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2410 instance,
2411 thread,
2412 fiber: resuming.fiber.take().unwrap(),
2413 });
2414 log::trace!("set next switch item to {item:?}");
2415 }
2416 };
2417 } else {
2418 log::trace!("resume_fiber: fiber has exited");
2419 }
2420
2421 Ok(())
2422 }
2423 }
2424
2425 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2431 log::trace!("suspend fiber: {reason:?}");
2432
2433 let state = self.concurrent_state_mut()?;
2434
2435 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2442 SuspendReason::Yielding { .. }
2443 | SuspendReason::Waiting { .. }
2444 | SuspendReason::ExplicitlySuspending { .. } => {
2445 if state.switch_item.is_none() {
2448 state.take_next_switch_item()?;
2449 }
2450
2451 (true, false)
2452 }
2453 SuspendReason::YieldingToSubtask { .. } => (true, true),
2454 SuspendReason::NeedWork => (false, false),
2455 };
2456
2457 let old_next_switch_item = if save_and_restore_next_switch_item {
2458 Some(state.save_next_switch_item()?)
2459 } else {
2460 None
2461 };
2462
2463 let old_guest_thread = if save_and_restore_thread {
2464 self.current_thread()?
2465 } else {
2466 CurrentThread::None
2467 };
2468
2469 let waiting_set = match &reason {
2470 SuspendReason::Waiting { set, .. } => Some(*set),
2471 _ => None,
2472 };
2473
2474 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2475 if suspend_reason.is_some() {
2476 bail_bug!("suspend reason unexpectedly already set for store");
2477 }
2478 *suspend_reason = Some(reason);
2479
2480 if !self.fiber_async_state_mut().can_block() {
2483 return Err(format_err!("future dropped"));
2484 }
2485
2486 if let Some(set) = waiting_set {
2489 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2490 }
2491
2492 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2493
2494 if let Some(set) = waiting_set {
2495 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2496 }
2497
2498 if save_and_restore_thread {
2499 self.set_thread(old_guest_thread)?;
2500 }
2501
2502 if let Some(item) = old_next_switch_item {
2503 let state = self.concurrent_state_mut()?;
2504 state.restore_next_switch_item(item)?;
2505 }
2506
2507 Ok(())
2508 }
2509
2510 fn wait_for_event(
2511 &mut self,
2512 caller_instance: RuntimeInstance,
2513 waitable: Waitable,
2514 ) -> Result<()> {
2515 let caller = self.current_guest_thread()?;
2516 let state = self.concurrent_state_mut()?;
2517
2518 waitable.trap_if_in_waitable_set(state)?;
2519
2520 let set = state.get_mut(caller.thread)?.sync_call_set;
2521 waitable.join(state, Some(set))?;
2522
2523 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2524
2525 self.suspend(SuspendReason::Waiting {
2526 set,
2527 thread: caller,
2528 })?;
2529 let state = self.concurrent_state_mut()?;
2530
2531 waitable.join(state, None)
2532 }
2533
2534 fn cleanup_thread(
2556 &mut self,
2557 guest_thread: QualifiedThreadId,
2558 runtime_instance: RuntimeInstance,
2559 ) -> Result<()> {
2560 let state = self.concurrent_state_mut()?;
2561 state.take_next_switch_item()?;
2564 let thread_data = state.get_mut(guest_thread.thread)?;
2565 let sync_call_set = thread_data.sync_call_set;
2566 if let Some(guest_id) = thread_data.instance_rep {
2567 let (state, counter) = self.instance_state_and_handle_counter(runtime_instance);
2568 state
2569 .thread_handle_table()
2570 .guest_thread_remove(counter, guest_id)?;
2571 }
2572 let state = self.concurrent_state_mut()?;
2573
2574 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2576 if let Some(Event::Subtask {
2577 status: Status::Returned | Status::ReturnCancelled,
2578 }) = waitable.common(state)?.event
2579 {
2580 waitable.delete_from(state)?;
2581 }
2582 }
2583
2584 state.delete(guest_thread.thread)?;
2585 state.delete(sync_call_set)?;
2586 let task = state.get_mut(guest_thread.task)?;
2587 task.threads.remove(&guest_thread.thread);
2588
2589 if task.threads.is_empty() && !task.returned_or_cancelled() {
2590 bail!(Trap::NoAsyncResult);
2591 }
2592
2593 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2594 task.decremented_interesting_task_count = true;
2595
2596 debug_assert!(state.interesting_tasks > 0);
2597 state.interesting_tasks -= 1;
2598 if state.interesting_tasks == 0
2599 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2600 {
2601 waker.wake();
2602 }
2603 }
2604
2605 state.decrement_task_ref_count(guest_thread.task)?;
2606
2607 Ok(())
2608 }
2609
2610 fn cancel_guest_subtask_without_lowered_parameters(
2619 &mut self,
2620 caller_instance: RuntimeInstance,
2621 guest_task: TableId<GuestTask>,
2622 ) -> Result<()> {
2623 let concurrent_state = self.concurrent_state_mut()?;
2624 let task = concurrent_state.get_mut(guest_task)?;
2625 assert!(!task.already_lowered_parameters());
2626 task.lower_params = None;
2630 task.lift_result = None;
2631 task.exited = true;
2632 let instance = task.instance;
2633
2634 assert_eq!(1, task.threads.len());
2637 let thread = *task.threads.iter().next().unwrap();
2638 self.cleanup_thread(
2639 QualifiedThreadId {
2640 task: guest_task,
2641 thread,
2642 },
2643 caller_instance,
2644 )?;
2645
2646 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2648 let pending_count = pending.len();
2649 pending.retain(|thread, _| thread.task != guest_task);
2650
2651 if pending.len() == pending_count {
2652 let matches = |item: &WorkItem| match item {
2655 WorkItem::ResumeThread { thread, .. }
2656 | WorkItem::GuestCall {
2657 call: GuestCall { thread, .. },
2658 ..
2659 } => thread.task == guest_task,
2660 _ => false,
2661 };
2662
2663 let remove = |queue: &mut VecDeque<WorkItem>| {
2664 let count = queue.len();
2665 queue.retain(|item| !matches(item));
2666 count != queue.len()
2667 };
2668
2669 let state = self.concurrent_state_mut()?;
2670 let mut found = remove(&mut state.high_priority);
2671 found |= remove(&mut state.low_priority);
2672
2673 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2674 if matches!(item, Some(item) if matches(item)) {
2675 found = true;
2676 *item = None;
2677 }
2678 }
2679
2680 if !found {
2681 bail!(Trap::SubtaskCancelAfterTerminal);
2682 }
2683 }
2684 Ok(())
2685 }
2686
2687 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2690 if !self.concurrency_support() {
2691 return Ok(self
2692 .current_scope_id_not_concurrent()?
2693 .map(|id| CurrentScope::Id(Scope::Id(id))));
2694 }
2695
2696 Ok(match self.current_thread()? {
2697 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2698 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2699 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2700 CurrentThread::None => return Ok(None),
2701 })
2702 }
2703
2704 pub(crate) fn queue_task(
2705 &mut self,
2706 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2707 ) -> Result<()> {
2708 self.concurrent_state_mut()?
2709 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2710 Ok(())
2711 }
2712
2713 fn any_may_not_suspend(&mut self) -> Result<bool> {
2722 Ok(self
2730 .concurrent_state_mut()?
2731 .table
2732 .get_mut()
2733 .iter_mut()
2734 .filter_map(|(_, entry)| {
2735 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2736 Some(task.instance)
2737 } else {
2738 None
2739 }
2740 })
2741 .collect::<Vec<_>>()
2742 .into_iter()
2743 .any(|instance| {
2744 self.instance_state(instance)
2745 .concurrent_state()
2746 .do_not_suspend
2747 }))
2748 }
2749}
2750
2751impl Instance {
2752 fn get_event(
2755 self,
2756 store: &mut StoreOpaque,
2757 guest_task: TableId<GuestTask>,
2758 set: Option<TableId<WaitableSet>>,
2759 cancellable: bool,
2760 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2761 let state = store.concurrent_state_mut()?;
2762
2763 let task = state.get_mut(guest_task)?;
2764 let event = &mut task.event;
2765 if let Some(ev) = event
2766 && (cancellable || !matches!(ev, Event::Cancelled))
2767 {
2768 log::trace!("deliver event {ev:?} to {guest_task:?}");
2769
2770 if matches!(ev, Event::Cancelled) {
2771 task.cancel_request_delivered = true;
2772 }
2773
2774 let ev = *ev;
2775 *event = None;
2776 return Ok(Some((ev, None)));
2777 }
2778
2779 let set = match set {
2780 Some(set) => set,
2781 None => return Ok(None),
2782 };
2783 let waitable = match state.get_mut(set)?.ready.pop_first() {
2784 Some(v) => v,
2785 None => return Ok(None),
2786 };
2787
2788 let common = waitable.common(state)?;
2789 let handle = match common.handle {
2790 Some(h) => h,
2791 None => bail_bug!("handle not set when delivering event"),
2792 };
2793 let event = match common.event.take() {
2794 Some(e) => e,
2795 None => bail_bug!("event not set when delivering event"),
2796 };
2797
2798 log::trace!(
2799 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2800 );
2801
2802 waitable.on_delivery(store, self, event)?;
2803
2804 Ok(Some((event, Some((waitable, handle)))))
2805 }
2806
2807 fn handle_callback_code(
2814 self,
2815 store: &mut dyn VMStore,
2816 guest_thread: QualifiedThreadId,
2817 runtime_instance: RuntimeInstance,
2818 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2819 ) -> Result<()> {
2820 let old_thread = store.set_thread(guest_thread)?;
2821 log::trace!(
2822 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2823 );
2824
2825 store.enter_instance(runtime_instance);
2826
2827 let mut code = call(store)?;
2828
2829 if matches!(
2835 unpack_callback_code(code).0,
2836 callback_code::YIELD | callback_code::WAIT
2837 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2838 {
2839 let (unpacked, set) = unpack_callback_code(code);
2840 log::trace!(
2841 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2842 cancellation; delivering it via callback"
2843 );
2844 code = invoke_callback(store, guest_thread.task, event, 0)?;
2845 }
2846
2847 store.exit_instance(runtime_instance)?;
2848
2849 store.set_thread(old_thread)?;
2850 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2851
2852 let store = store.store_opaque_mut();
2853 let (code, set) = unpack_callback_code(code);
2854
2855 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2856
2857 let state = store.concurrent_state_mut()?;
2858
2859 state.take_next_switch_item()?;
2860
2861 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2862 let set = store
2863 .instance_state(runtime_instance)
2864 .handle_table()
2865 .waitable_set_rep(handle)?;
2866
2867 Ok(TableId::<WaitableSet>::new(set))
2868 };
2869
2870 match code {
2871 callback_code::EXIT => {
2872 log::trace!("implicit thread {guest_thread:?} completed");
2873 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2874 task.exited = true;
2875 task.callback = None;
2876
2877 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2882
2883 store.cleanup_thread(guest_thread, runtime_instance)?;
2884 }
2885 callback_code::YIELD => {
2886 let old = state
2889 .get_mut(guest_thread.thread)?
2890 .wake_on_cancel
2891 .replace(WakeOnCancel::Yielding);
2892 if !old.is_none() {
2893 bail_bug!("thread unexpectedly had wake_on_cancel set");
2894 }
2895
2896 let call = GuestCall {
2903 thread: guest_thread,
2904 kind: GuestCallKind::DeliverEvent {
2905 instance: self,
2906 set: None,
2907 },
2908 };
2909 state.push_low_priority(WorkItem::GuestCall {
2912 instance: runtime_instance,
2913 call,
2914 });
2915 }
2916 callback_code::WAIT => {
2917 let set = get_set(store, set)?;
2918 self.wait_with_callback(store, guest_thread, set)?;
2919 }
2920 _ => bail!(Trap::UnsupportedCallbackCode),
2921 }
2922
2923 Ok(())
2924 }
2925
2926 fn wait_with_callback(
2932 self,
2933 store: &mut StoreOpaque,
2934 guest_thread: QualifiedThreadId,
2935 set: TableId<WaitableSet>,
2936 ) -> Result<()> {
2937 let state = store.concurrent_state_mut()?;
2938 state.get_mut(set)?.num_waiting += 1;
2941
2942 if state.get_mut(guest_thread.task)?.event.is_some()
2943 || !state.get_mut(set)?.ready.is_empty()
2944 {
2945 let instance = state.get_mut(guest_thread.task)?.instance;
2947 state.push_high_priority(WorkItem::GuestCall {
2948 instance,
2949 call: GuestCall {
2950 thread: guest_thread,
2951 kind: GuestCallKind::DeliverEvent {
2952 instance: self,
2953 set: Some(set),
2954 },
2955 },
2956 });
2957 return Ok(());
2958 }
2959
2960 let instance = store
2966 .concurrent_state_mut()?
2967 .get_mut(guest_thread.task)?
2968 .instance;
2969 store.switch_or_trap_if_may_not_suspend(instance)?;
2970
2971 let state = store.concurrent_state_mut()?;
2977 let old = state
2978 .get_mut(guest_thread.thread)?
2979 .wake_on_cancel
2980 .replace(WakeOnCancel::Waiting(set));
2981 if !old.is_none() {
2982 bail_bug!("thread unexpectedly had wake_on_cancel set");
2983 }
2984 let old = state
2985 .get_mut(set)?
2986 .waiting
2987 .insert(guest_thread, WaitMode::Callback(self));
2988 if !old.is_none() {
2989 bail_bug!("set's waiting set already had this thread registered");
2990 }
2991 Ok(())
2992 }
2993
2994 unsafe fn stage_call<T: 'static>(
3001 self,
3002 mut store: StoreContextMut<T>,
3003 guest_thread: QualifiedThreadId,
3004 callee: SendSyncPtr<VMFuncRef>,
3005 param_count: usize,
3006 result_count: usize,
3007 async_: bool,
3008 callback: Option<SendSyncPtr<VMFuncRef>>,
3009 post_return: Option<SendSyncPtr<VMFuncRef>>,
3010 host_caller: bool,
3011 ) -> Result<()> {
3012 unsafe fn make_call<T: 'static>(
3027 store: StoreContextMut<T>,
3028 guest_thread: QualifiedThreadId,
3029 callee: SendSyncPtr<VMFuncRef>,
3030 param_count: usize,
3031 result_count: usize,
3032 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3033 + Send
3034 + Sync
3035 + 'static
3036 + use<T> {
3037 let token = StoreToken::new(store);
3038 move |store: &mut dyn VMStore| {
3039 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3040
3041 store
3042 .concurrent_state_mut()?
3043 .set_thread_running(guest_thread.thread)?;
3044 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3045 let lower = match task.lower_params.take() {
3046 Some(l) => l,
3047 None => bail_bug!("lower_params missing"),
3048 };
3049
3050 lower(store, &mut storage[..param_count])?;
3051
3052 let mut store = token.as_context_mut(store);
3053
3054 unsafe {
3057 crate::Func::call_unchecked_raw(
3058 &mut store,
3059 callee.as_non_null(),
3060 NonNull::new(
3061 &mut storage[..param_count.max(result_count)]
3062 as *mut [MaybeUninit<ValRaw>] as _,
3063 )
3064 .unwrap(),
3065 UncaughtException::Trap,
3066 )?;
3067 }
3068
3069 Ok(storage)
3070 }
3071 }
3072
3073 let call = unsafe {
3077 make_call(
3078 store.as_context_mut(),
3079 guest_thread,
3080 callee,
3081 param_count,
3082 result_count,
3083 )
3084 };
3085
3086 let callee_instance = store
3087 .0
3088 .concurrent_state_mut()?
3089 .get_mut(guest_thread.task)?
3090 .instance;
3091
3092 let fun = if callback.is_some() {
3093 assert!(async_);
3094
3095 Box::new(move |store: &mut dyn VMStore| {
3096 self.add_guest_thread_to_instance_table(
3097 guest_thread.thread,
3098 store,
3099 callee_instance.index,
3100 )?;
3101 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3102 let storage = call(store)?;
3103
3104 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3107 })
3108 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3109 } else {
3110 let token = StoreToken::new(store.as_context_mut());
3111 Box::new(move |store: &mut dyn VMStore| {
3112 self.add_guest_thread_to_instance_table(
3113 guest_thread.thread,
3114 store,
3115 callee_instance.index,
3116 )?;
3117 let old_thread = store.set_thread(guest_thread)?;
3118 log::trace!(
3119 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3120 );
3121 let flags = self.id().get(store).instance_flags(callee_instance.index);
3122
3123 let callee_async_typed = store
3124 .concurrent_state_mut()?
3125 .get_mut(guest_thread.task)?
3126 .async_typed;
3127
3128 if !async_ && callee_async_typed {
3132 store.enter_instance(callee_instance);
3133 }
3134
3135 if !callee_async_typed {
3136 store.enter_sync_call(callee_instance)?;
3137 }
3138
3139 let storage = call(store)?;
3146
3147 if !callee_async_typed {
3148 store.exit_sync_call(callee_instance)?;
3149 }
3150
3151 if !async_ {
3152 if callee_async_typed {
3158 store.exit_instance(callee_instance)?;
3159 }
3160
3161 let lift = {
3162 let state = store.concurrent_state_mut()?;
3163 if !state.get_mut(guest_thread.task)?.result.is_none() {
3164 bail_bug!("task has already produced a result");
3165 }
3166
3167 match state.get_mut(guest_thread.task)?.lift_result.take() {
3168 Some(lift) => lift,
3169 None => bail_bug!("lift_result field is missing"),
3170 }
3171 };
3172
3173 let result = (lift.lift)(store, unsafe {
3176 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3177 &storage[..result_count],
3178 )
3179 })?;
3180
3181 let post_return_arg = match result_count {
3182 0 => ValRaw::i32(0),
3183 1 => unsafe { storage[0].assume_init() },
3186 _ => unreachable!(),
3187 };
3188
3189 unsafe {
3190 call_post_return(
3191 token.as_context_mut(store),
3192 post_return.map(|v| v.as_non_null()),
3193 post_return_arg,
3194 flags,
3195 )?;
3196 }
3197
3198 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3199 }
3200
3201 store.set_thread(old_thread)?;
3202
3203 store
3204 .concurrent_state_mut()?
3205 .get_mut(guest_thread.task)?
3206 .exited = true;
3207
3208 log::trace!(
3209 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3210 );
3211
3212 if callee_async_typed {
3213 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3218 }
3219
3220 store.cleanup_thread(guest_thread, callee_instance)?;
3222 Ok(())
3223 })
3224 };
3225
3226 store.0.concurrent_state_mut()?.push_work_item(
3227 WorkItem::GuestCall {
3228 instance: callee_instance,
3229 call: GuestCall {
3230 thread: guest_thread,
3231 kind: GuestCallKind::StartImplicit(fun),
3232 },
3233 },
3234 if host_caller {
3235 Priority::High
3236 } else {
3237 Priority::Switch
3238 },
3239 )?;
3240
3241 Ok(())
3242 }
3243
3244 unsafe fn prepare_call<T: 'static>(
3257 self,
3258 mut store: StoreContextMut<T>,
3259 start: NonNull<VMFuncRef>,
3260 return_: NonNull<VMFuncRef>,
3261 caller_instance: RuntimeComponentInstanceIndex,
3262 callee_instance: RuntimeComponentInstanceIndex,
3263 task_return_type: TypeTupleIndex,
3264 callee_async_typed: bool,
3265 memory: *mut VMMemoryDefinition,
3266 string_encoding: StringEncoding,
3267 caller_info: CallerInfo,
3268 ) -> Result<()> {
3269 enum ResultInfo {
3270 Heap { results: u32 },
3271 Stack { result_count: u32 },
3272 }
3273
3274 let result_info = match &caller_info {
3275 CallerInfo::Async {
3276 has_result: true,
3277 params,
3278 } => ResultInfo::Heap {
3279 results: match params.last() {
3281 Some(r) => r.get_u32(),
3282 None => bail_bug!("retptr missing"),
3283 },
3284 },
3285 CallerInfo::Async {
3286 has_result: false, ..
3287 } => ResultInfo::Stack { result_count: 0 },
3288 CallerInfo::Sync {
3289 result_count,
3290 params,
3291 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3292 results: match params.last() {
3294 Some(r) => r.get_u32(),
3295 None => bail_bug!("arg ptr missing"),
3296 },
3297 },
3298 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3299 result_count: *result_count,
3300 },
3301 };
3302
3303 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3304
3305 let start = SendSyncPtr::new(start);
3309 let return_ = SendSyncPtr::new(return_);
3310 let token = StoreToken::new(store.as_context_mut());
3311 let old_thread = store.0.current_guest_thread()?;
3312
3313 let state = store.0.concurrent_state_mut()?;
3314
3315 debug_assert_eq!(
3316 state.get_mut(old_thread.task)?.instance,
3317 self.runtime_instance(caller_instance)
3318 );
3319
3320 let guest_thread = GuestTask::new(
3321 state,
3322 Box::new(move |store, dst| {
3323 let mut store = token.as_context_mut(store);
3324 assert!(dst.len() <= MAX_FLAT_PARAMS);
3325 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3327 let count = match caller_info {
3328 CallerInfo::Async { params, has_result } => {
3332 let params = ¶ms[..params.len() - usize::from(has_result)];
3333 for (param, src) in params.iter().zip(&mut src) {
3334 src.write(*param);
3335 }
3336 params.len()
3337 }
3338
3339 CallerInfo::Sync { params, .. } => {
3341 for (param, src) in params.iter().zip(&mut src) {
3342 src.write(*param);
3343 }
3344 params.len()
3345 }
3346 };
3347 unsafe {
3354 crate::Func::call_unchecked_raw(
3355 &mut store,
3356 start.as_non_null(),
3357 NonNull::new(
3358 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3359 )
3360 .unwrap(),
3361 UncaughtException::Trap,
3362 )?;
3363 }
3364 dst.copy_from_slice(&src[..dst.len()]);
3365 let task = store.0.current_guest_thread()?.task;
3366 let state = store.0.concurrent_state_mut()?;
3367 Waitable::Guest(task).set_event(
3368 state,
3369 Some(Event::Subtask {
3370 status: Status::Started,
3371 }),
3372 )?;
3373 Ok(())
3374 }),
3375 LiftResult {
3376 lift: Box::new(move |store, src| {
3377 let mut store = token.as_context_mut(store);
3380 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3382 my_src.push(ValRaw::u32(*results));
3383 }
3384
3385 unsafe {
3392 crate::Func::call_unchecked_raw(
3393 &mut store,
3394 return_.as_non_null(),
3395 my_src.as_mut_slice().into(),
3396 UncaughtException::Trap,
3397 )?;
3398 }
3399
3400 let thread = store.0.current_guest_thread()?;
3401 let state = store.0.concurrent_state_mut()?;
3402 if sync_caller {
3403 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3404 if let ResultInfo::Stack { result_count } = &result_info {
3405 match result_count {
3406 0 => None,
3407 1 => Some(my_src[0]),
3408 _ => unreachable!(),
3409 }
3410 } else {
3411 None
3412 },
3413 );
3414 }
3415 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3416 }),
3417 ty: task_return_type,
3418 memory: NonNull::new(memory).map(SendSyncPtr::new),
3419 string_encoding,
3420 },
3421 Caller::Guest { thread: old_thread },
3422 None,
3423 self.runtime_instance(callee_instance),
3424 callee_async_typed,
3425 false,
3428 )?;
3429
3430 store.0.set_thread(guest_thread)?;
3433 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3434
3435 Ok(())
3436 }
3437
3438 unsafe fn call_callback<T>(
3443 self,
3444 mut store: StoreContextMut<T>,
3445 function: SendSyncPtr<VMFuncRef>,
3446 event: Event,
3447 handle: u32,
3448 ) -> Result<u32> {
3449 let (ordinal, result) = event.parts();
3450 let params = &mut [
3451 ValRaw::u32(ordinal),
3452 ValRaw::u32(handle),
3453 ValRaw::u32(result),
3454 ];
3455 unsafe {
3460 crate::Func::call_unchecked_raw(
3461 &mut store,
3462 function.as_non_null(),
3463 params.as_mut_slice().into(),
3464 UncaughtException::Trap,
3465 )?;
3466 }
3467 Ok(params[0].get_u32())
3468 }
3469
3470 unsafe fn start_call<T: 'static>(
3488 self,
3489 mut store: StoreContextMut<T>,
3490 callback: *mut VMFuncRef,
3491 post_return: *mut VMFuncRef,
3492 callee: NonNull<VMFuncRef>,
3493 param_count: u32,
3494 result_count: u32,
3495 flags: u32,
3496 storage: &mut [MaybeUninit<ValRaw>],
3497 ) -> Result<()> {
3498 let token = StoreToken::new(store.as_context_mut());
3499 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3500 let guest_thread = store.0.current_guest_thread()?;
3501 let state = store.0.concurrent_state_mut()?;
3502
3503 if !state.event_loop_running {
3504 bail_bug!("Instance::start_call called without a running event loop");
3505 }
3506
3507 let callee = SendSyncPtr::new(callee);
3508 let param_count = usize::try_from(param_count)?;
3509 assert!(param_count <= MAX_FLAT_PARAMS);
3510 let result_count = usize::try_from(result_count)?;
3511 assert!(result_count <= MAX_FLAT_RESULTS);
3512
3513 let task = state.get_mut(guest_thread.task)?;
3514 let callee_async_typed = task.async_typed;
3515 let callee_instance = task.instance;
3516
3517 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3518
3519 if let Some(callback) = NonNull::new(callback) {
3520 let callback = SendSyncPtr::new(callback);
3524 task.callback = Some(Box::new(move |store, event, handle| {
3525 let store = token.as_context_mut(store);
3526 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3527 }));
3528 }
3529
3530 let Caller::Guest { thread: caller } = &task.caller else {
3531 bail_bug!("start_call unexpectedly invoked for host->guest call");
3534 };
3535 let caller = *caller;
3536 let caller_instance = state.get_mut(caller.task)?.instance;
3537
3538 unsafe {
3540 self.stage_call(
3541 store.as_context_mut(),
3542 guest_thread,
3543 callee,
3544 param_count,
3545 result_count,
3546 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3547 NonNull::new(callback).map(SendSyncPtr::new),
3548 NonNull::new(post_return).map(SendSyncPtr::new),
3549 false,
3550 )?;
3551 }
3552
3553 let old_do_not_suspend = if callee_async_typed {
3554 let state = store.0.instance_state(callee_instance).concurrent_state();
3561 let old_do_not_suspend = state.do_not_suspend;
3562 state.do_not_suspend = false;
3563 Some(old_do_not_suspend)
3564 } else {
3565 None
3566 };
3567
3568 let state = store.0.concurrent_state_mut()?;
3569
3570 let guest_waitable = Waitable::Guest(guest_thread.task);
3573 let old_set = guest_waitable.common(state)?.set;
3574 let set = state.get_mut(caller.thread)?.sync_call_set;
3575 guest_waitable.join(state, Some(set))?;
3576
3577 store.0.set_thread(CurrentThread::None)?;
3578
3579 let mut yielded = false;
3595 let (status, waitable) = loop {
3596 store.0.suspend(if yielded {
3597 SuspendReason::Waiting {
3598 set,
3599 thread: caller,
3600 }
3601 } else {
3602 yielded = true;
3603 SuspendReason::YieldingToSubtask { thread: caller }
3604 })?;
3605
3606 if let Some(old_do_not_suspend) = old_do_not_suspend {
3607 store
3608 .0
3609 .instance_state(callee_instance)
3610 .concurrent_state()
3611 .do_not_suspend = old_do_not_suspend;
3612 }
3613
3614 let state = store.0.concurrent_state_mut()?;
3615
3616 log::trace!("taking event for {:?}", guest_thread.task);
3617 let event = guest_waitable.take_event(state)?;
3618 let Some(Event::Subtask { status }) = event else {
3619 bail_bug!("subtasks should only get subtask events, got {event:?}")
3620 };
3621
3622 log::trace!("status {status:?} for {:?}", guest_thread.task);
3623
3624 if status == Status::Returned {
3625 break (status, None);
3627 } else if async_caller {
3628 let (state, counter) = store.0.instance_state_and_handle_counter(caller_instance);
3632 let handle = state
3633 .handle_table()
3634 .subtask_insert_guest(counter, guest_thread.task.rep())?;
3635 store
3636 .0
3637 .concurrent_state_mut()?
3638 .get_mut(guest_thread.task)?
3639 .common
3640 .handle = Some(handle);
3641 break (status, Some(handle));
3642 } else {
3643 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3647 }
3648 };
3649
3650 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3651
3652 store.0.set_thread(caller)?;
3654
3655 store
3656 .0
3657 .concurrent_state_mut()?
3658 .set_thread_running(caller.thread)?;
3659
3660 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3661
3662 if async_caller {
3663 let Some(slot) = storage.first_mut() else {
3664 bail_bug!("no storage for async call status");
3665 };
3666 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3667 } else {
3668 let state = store.0.concurrent_state_mut()?;
3671 let task = state.get_mut(guest_thread.task)?;
3672 if let Some(Some(result)) = task.sync_result.take()? {
3673 storage[0] = MaybeUninit::new(result);
3674 }
3675 }
3676
3677 if status == Status::Returned {
3678 store
3679 .0
3680 .concurrent_state_mut()?
3681 .decrement_task_ref_count(guest_thread.task)?;
3682 }
3683
3684 Ok(())
3685 }
3686
3687 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3703 self,
3704 mut store: StoreContextMut<'_, T>,
3705 host_task: EnteredHostTask,
3706 result_may_require_realloc: bool,
3707 future: impl Future<Output = Result<R>> + Send + 'static,
3708 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3709 + Send
3710 + 'static,
3711 ) -> Result<u32> {
3712 let token = StoreToken::new(store.as_context_mut());
3713
3714 let (join_handle, future) = JoinHandle::run(future);
3717 let mut future = Box::pin(future);
3718
3719 let poll = tls::set(store.0, || {
3724 future
3725 .as_mut()
3726 .poll(&mut Context::from_waker(&Waker::noop()))
3727 });
3728
3729 match poll {
3730 Poll::Ready(result) => {
3732 let result = result.transpose()?;
3733 let task = store.0.current_materialized_host_task()?;
3736 lower(store.as_context_mut(), result, true, task)?;
3737 return Ok(Status::Returned.pack(None));
3738 }
3739
3740 Poll::Pending => {}
3742 }
3743
3744 let Some(task) = store.0.materialize_host_task_id()? else {
3748 bail_bug!("current thread is not a host thread")
3749 };
3750 {
3751 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3752 assert!(matches!(state, HostTaskState::CalleeStarted));
3753 *state = HostTaskState::CalleeRunning(join_handle);
3754 }
3755
3756 let future = Box::pin(async move {
3764 let result = match run_with_host_task_set(task, future).await? {
3765 Some(result) => Some(result?),
3766 None => None,
3767 };
3768 let on_complete = move |store: &mut dyn VMStore| {
3769 let mut store = token.as_context_mut(store);
3773 let old = store.0.set_thread(task)?;
3774
3775 let status = if result.is_some() {
3776 Status::Returned
3777 } else {
3778 Status::ReturnCancelled
3779 };
3780
3781 lower(store.as_context_mut(), result, false, Some(task))?;
3782 let state = store.0.concurrent_state_mut()?;
3783 match &mut state.get_mut(task)?.state {
3784 pending @ HostTaskState::CalleeCancelling => {
3787 *pending = HostTaskState::CalleeDone { cancelled: true };
3788 }
3789
3790 other => *other = HostTaskState::CalleeDone { cancelled: false },
3792 }
3793 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3794
3795 store.0.set_thread(old)?;
3796 Ok(())
3797 };
3798
3799 tls::get(move |store| {
3800 if result_may_require_realloc {
3801 store
3806 .concurrent_state_mut()?
3807 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3808 on_complete,
3809 ))));
3810 Ok(())
3811 } else {
3812 on_complete(store)
3815 }
3816 })
3817 });
3818
3819 let caller = match host_task {
3822 Some(caller) => caller,
3823 None => bail_bug!("host task wasn't created but should have been"),
3824 };
3825 let state = store.0.concurrent_state_mut()?;
3826 state.push_future(future);
3827 let instance = state.get_mut(caller.task)?.instance;
3828 let (state, counter) = store.0.instance_state_and_handle_counter(instance);
3829 let handle = state
3830 .handle_table()
3831 .subtask_insert_host(counter, task.rep())?;
3832 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3833 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3834
3835 store.0.set_thread(caller)?;
3839 Ok(Status::Started.pack(Some(handle)))
3840 }
3841
3842 pub(crate) fn task_return(
3845 self,
3846 store: &mut dyn VMStore,
3847 ty: TypeTupleIndex,
3848 options: OptionsIndex,
3849 storage: &[ValRaw],
3850 ) -> Result<()> {
3851 let guest_thread = store.current_guest_thread()?;
3852 let state = store.concurrent_state_mut()?;
3853 if !state.get_mut(guest_thread.task)?.async_lifted {
3854 bail!(Trap::TaskReturnOrCancelSyncLifted);
3855 }
3856 let lift = state
3857 .get_mut(guest_thread.task)?
3858 .lift_result
3859 .take()
3860 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3861 if !state.get_mut(guest_thread.task)?.result.is_none() {
3862 bail_bug!("task result unexpectedly already set");
3863 }
3864
3865 let CanonicalOptions {
3866 string_encoding,
3867 data_model,
3868 ..
3869 } = &self.id().get(store).component().env_component().options[options];
3870
3871 let invalid = ty != lift.ty
3872 || string_encoding != &lift.string_encoding
3873 || match data_model {
3874 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3875 Some(memory) => {
3876 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3877 let actual = self.id().get(store).runtime_memory(memory);
3878 expected != actual.as_ptr()
3879 }
3880 None => false,
3883 },
3884 CanonicalOptionsDataModel::Gc { .. } => true,
3886 };
3887
3888 if invalid {
3889 bail!(Trap::TaskReturnInvalid);
3890 }
3891
3892 log::trace!("task.return for {guest_thread:?}");
3893
3894 let result = (lift.lift)(store, storage)?;
3895 self.task_complete(store, guest_thread.task, result, Status::Returned)
3896 }
3897
3898 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3900 let guest_thread = store.current_guest_thread()?;
3901 let state = store.concurrent_state_mut()?;
3902 let task = state.get_mut(guest_thread.task)?;
3903 if !task.async_lifted {
3904 bail!(Trap::TaskReturnOrCancelSyncLifted);
3905 }
3906 if !task.cancel_request_delivered {
3907 bail!(Trap::TaskCancelNotCancelled);
3908 }
3909 _ = task
3910 .lift_result
3911 .take()
3912 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3913
3914 if !task.result.is_none() {
3915 bail_bug!("task result should not bet set yet");
3916 }
3917
3918 log::trace!("task.cancel for {guest_thread:?}");
3919
3920 self.task_complete(
3921 store,
3922 guest_thread.task,
3923 Box::new(DummyResult),
3924 Status::ReturnCancelled,
3925 )
3926 }
3927
3928 fn task_complete(
3934 self,
3935 store: &mut StoreOpaque,
3936 guest_task: TableId<GuestTask>,
3937 result: Box<dyn Any + Send + Sync>,
3938 status: Status,
3939 ) -> Result<()> {
3940 store
3941 .component_resource_tables(Some(self))?
3942 .validate_scope_exit()?;
3943
3944 let state = store.concurrent_state_mut()?;
3945 let task = state.get_mut(guest_task)?;
3946
3947 task.event = None;
3951
3952 if let Caller::Host { tx, .. } = &mut task.caller {
3953 if let Some(tx) = tx.take() {
3954 _ = tx.send(result);
3955 }
3956 } else {
3957 task.result = Some(result);
3958 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3959 }
3960
3961 Ok(())
3962 }
3963
3964 pub(crate) fn waitable_set_new(
3966 self,
3967 store: &mut StoreOpaque,
3968 caller_instance: RuntimeComponentInstanceIndex,
3969 ) -> Result<u32> {
3970 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3971 let (state, counter) =
3972 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3973 let handle = state
3974 .handle_table()
3975 .waitable_set_insert(counter, set.rep())?;
3976 log::trace!("new waitable set {set:?} (handle {handle})");
3977 Ok(handle)
3978 }
3979
3980 pub(crate) fn waitable_set_drop(
3982 self,
3983 store: &mut StoreOpaque,
3984 caller_instance: RuntimeComponentInstanceIndex,
3985 set: u32,
3986 ) -> Result<()> {
3987 let (state, counter) =
3988 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3989 let rep = state.handle_table().waitable_set_remove(counter, set)?;
3990
3991 log::trace!("drop waitable set {rep} (handle {set})");
3992
3993 let set = store
3997 .concurrent_state_mut()?
3998 .get_mut(TableId::<WaitableSet>::new(rep))?;
3999 if set.num_waiting > 0 {
4000 bail!(Trap::WaitableSetDropHasWaiters);
4001 }
4002
4003 store
4004 .concurrent_state_mut()?
4005 .delete(TableId::<WaitableSet>::new(rep))?;
4006
4007 Ok(())
4008 }
4009
4010 pub(crate) fn waitable_join(
4012 self,
4013 store: &mut StoreOpaque,
4014 caller_instance: RuntimeComponentInstanceIndex,
4015 waitable_handle: u32,
4016 set_handle: u32,
4017 ) -> Result<()> {
4018 let mut instance = self.id().get_mut(store);
4019 let waitable =
4020 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4021
4022 let set = if set_handle == 0 {
4023 None
4024 } else {
4025 let set = instance.instance_states().0[caller_instance]
4026 .handle_table()
4027 .waitable_set_rep(set_handle)?;
4028
4029 Some(TableId::<WaitableSet>::new(set))
4030 };
4031
4032 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4033
4034 log::trace!(
4035 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4036 );
4037
4038 waitable.join(store.concurrent_state_mut()?, set)
4039 }
4040
4041 pub(crate) fn subtask_drop(
4043 self,
4044 store: &mut StoreOpaque,
4045 caller_instance: RuntimeComponentInstanceIndex,
4046 task_id: u32,
4047 ) -> Result<()> {
4048 self.waitable_join(store, caller_instance, task_id, 0)?;
4049
4050 let (state, counter) =
4051 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
4052 let (rep, is_host) = state.handle_table().subtask_remove(counter, task_id)?;
4053
4054 let concurrent_state = store.concurrent_state_mut()?;
4055 let waitable = if is_host {
4056 let id = TableId::<HostTask>::new(rep);
4057 let task = concurrent_state.get_mut(id)?;
4058 match &task.state {
4059 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4060 bail!(Trap::SubtaskDropNotResolved)
4061 }
4062 HostTaskState::CalleeDone { .. } => {}
4063 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4064 bail_bug!("invalid state for callee in `subtask.drop`")
4065 }
4066 }
4067
4068 Waitable::Host(id)
4069 } else {
4070 let id = TableId::<GuestTask>::new(rep);
4071 let task = concurrent_state.get_mut(id)?;
4072 if task.lift_result.is_some() {
4073 bail!(Trap::SubtaskDropNotResolved);
4074 }
4075 Waitable::Guest(id)
4076 };
4077
4078 waitable.common(concurrent_state)?.handle = None;
4079
4080 if waitable.take_event(concurrent_state)?.is_some() {
4083 bail!(Trap::SubtaskDropNotResolved);
4084 }
4085
4086 waitable.delete_from(concurrent_state)?;
4087
4088 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4089 Ok(())
4090 }
4091
4092 pub(crate) fn waitable_set_wait(
4094 self,
4095 store: &mut StoreOpaque,
4096 options: OptionsIndex,
4097 set: u32,
4098 payload: u32,
4099 ) -> Result<u32> {
4100 let &CanonicalOptions {
4101 instance: caller_instance,
4102 ..
4103 } = &self.id().get(store).component().env_component().options[options];
4104 let caller = self.runtime_instance(caller_instance);
4105 let rep = store
4106 .instance_state(self.runtime_instance(caller_instance))
4107 .handle_table()
4108 .waitable_set_rep(set)?;
4109
4110 self.waitable_check(
4111 store,
4112 caller,
4113 WaitableCheck::Wait,
4114 WaitableCheckParams {
4115 set: TableId::new(rep),
4116 options,
4117 payload,
4118 },
4119 )
4120 }
4121
4122 pub(crate) fn waitable_set_poll(
4124 self,
4125 store: &mut StoreOpaque,
4126 options: OptionsIndex,
4127 set: u32,
4128 payload: u32,
4129 ) -> Result<u32> {
4130 let &CanonicalOptions {
4131 instance: caller_instance,
4132 ..
4133 } = &self.id().get(store).component().env_component().options[options];
4134 let caller = self.runtime_instance(caller_instance);
4135 let rep = store
4136 .instance_state(caller)
4137 .handle_table()
4138 .waitable_set_rep(set)?;
4139
4140 self.waitable_check(
4141 store,
4142 caller,
4143 WaitableCheck::Poll,
4144 WaitableCheckParams {
4145 set: TableId::new(rep),
4146 options,
4147 payload,
4148 },
4149 )
4150 }
4151
4152 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4154 let thread_id = store.current_guest_thread()?.thread;
4155 match store
4156 .concurrent_state_mut()?
4157 .get_mut(thread_id)?
4158 .instance_rep
4159 {
4160 Some(r) => Ok(r),
4161 None => bail_bug!("thread should have instance_rep by now"),
4162 }
4163 }
4164
4165 pub(crate) fn thread_new_indirect<T: 'static>(
4167 self,
4168 mut store: StoreContextMut<T>,
4169 runtime_instance: RuntimeComponentInstanceIndex,
4170 start_func_ty: ModuleInternedTypeIndex,
4171 start_func_table_idx: RuntimeTableIndex,
4172 start_func_idx: u32,
4173 context: i32,
4174 ) -> Result<u32> {
4175 log::trace!("creating new thread");
4176
4177 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4178 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4179 else {
4180 bail_bug!("thread.new-indirect start function type should be registered");
4181 };
4182 let callee = instance
4183 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4184 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4185 if callee.type_index(store.0) != start_func_ty {
4186 bail!(Trap::ThreadNewIndirectInvalidType);
4187 }
4188
4189 let token = StoreToken::new(store.as_context_mut());
4190 let start_func = Box::new(
4191 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4192 let old_thread = store.set_thread(guest_thread)?;
4193 log::trace!(
4194 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4195 );
4196
4197 let mut store = token.as_context_mut(store);
4198 let mut params = [ValRaw::i32(context)];
4199 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4202
4203 store.0.set_thread(old_thread)?;
4204
4205 let runtime_instance = self.runtime_instance(runtime_instance);
4206
4207 store
4210 .0
4211 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4212
4213 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4214
4215 log::trace!("explicit thread {guest_thread:?} completed");
4216 let state = store.0.concurrent_state_mut()?;
4217 if let Some(t) = old_thread.guest() {
4218 state.set_thread_running(t.thread)?;
4219 }
4220 log::trace!("thread start: restored {old_thread:?} as current thread");
4221
4222 Ok(())
4223 },
4224 );
4225
4226 let current_thread = store.0.current_guest_thread()?;
4227 let state = store.0.concurrent_state_mut()?;
4228 let parent_task = current_thread.task;
4229
4230 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4231 let thread_id = state.push(new_thread)?;
4232 state.get_mut(parent_task)?.threads.insert(thread_id);
4233
4234 log::trace!("new thread with id {thread_id:?} created");
4235
4236 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4237 }
4238
4239 pub(crate) fn resume_thread(
4240 self,
4241 store: &mut StoreOpaque,
4242 runtime_instance: RuntimeComponentInstanceIndex,
4243 thread_idx: u32,
4244 how: ResumeThread,
4245 ) -> Result<bool> {
4246 let thread_id =
4247 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4248 let state = store.concurrent_state_mut()?;
4249 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4250
4251 if store.current_guest_thread()? == guest_thread {
4252 bail!(Trap::CannotResumeThread);
4253 }
4254
4255 let priority = match how {
4256 ResumeThread::Resume => Priority::Switch,
4257 ResumeThread::ResumeLater => Priority::Low,
4258
4259 ResumeThread::Promote => {
4262 let instance = self.runtime_instance(runtime_instance);
4263 let do_not_enter = store
4264 .instance_state(instance)
4265 .concurrent_state()
4266 .do_not_enter;
4267 return store.concurrent_state_mut()?.promote_work_item_matching(
4268 |item: &WorkItem| match item {
4269 WorkItem::ResumeThread { thread, .. }
4270 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4271
4272 WorkItem::GuestCall {
4273 call: GuestCall { thread, kind },
4274 ..
4275 } => {
4276 *thread == guest_thread
4277 && match kind {
4278 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4282
4283 GuestCallKind::StartExplicit(_) => true,
4286
4287 GuestCallKind::StartImplicit(_) => false,
4291 }
4292 }
4293
4294 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4295 },
4296 );
4297 }
4298 };
4299
4300 let state = store.concurrent_state_mut()?;
4301 let thread = state.get_mut(guest_thread.thread)?;
4302
4303 match &thread.state {
4306 GuestThreadState::NotStartedExplicit(_) => {}
4307 GuestThreadState::Suspended(_) => {
4308 log::trace!("resuming thread {thread_id:?} that was suspended");
4309 state.schedule_suspended_thread(
4310 guest_thread,
4311 self.runtime_instance(runtime_instance),
4312 priority,
4313 )?;
4314 return Ok(true);
4315 }
4316 _ => bail!(Trap::CannotResumeThread),
4317 }
4318 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4319 GuestThreadState::NotStartedExplicit(start_func) => {
4320 log::trace!("starting thread {guest_thread:?}");
4321 let guest_call = WorkItem::GuestCall {
4322 instance: self.runtime_instance(runtime_instance),
4323 call: GuestCall {
4324 thread: guest_thread,
4325 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4326 start_func(store, guest_thread)
4327 })),
4328 },
4329 };
4330 state.push_work_item(guest_call, priority)?;
4331 }
4332 other => {
4333 thread.state = other;
4334 bail_bug!("thread state checked to be resumable above");
4335 }
4336 }
4337 Ok(true)
4338 }
4339
4340 fn add_guest_thread_to_instance_table(
4341 self,
4342 thread_id: TableId<GuestThread>,
4343 store: &mut StoreOpaque,
4344 runtime_instance: RuntimeComponentInstanceIndex,
4345 ) -> Result<u32> {
4346 let (state, counter) =
4347 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4348 let guest_id = state
4349 .thread_handle_table()
4350 .guest_thread_insert(counter, thread_id.rep())?;
4351 store
4352 .concurrent_state_mut()?
4353 .get_mut(thread_id)?
4354 .instance_rep = Some(guest_id);
4355 Ok(guest_id)
4356 }
4357
4358 pub(crate) fn suspension_intrinsic(
4362 self,
4363 store: &mut StoreOpaque,
4364 caller: RuntimeComponentInstanceIndex,
4365 yielding: bool,
4366 to_thread: SuspensionTarget,
4367 ) -> Result<WaitResult> {
4368 let check_suspend = match to_thread {
4369 SuspensionTarget::Promote(thread) => {
4370 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4371 }
4372 SuspensionTarget::Resume(thread) => {
4373 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4374 bail_bug!(
4375 "`resume_thread` should only ever return false \
4376 when `ResumeThread::Promote` is passed to it"
4377 );
4378 }
4379 false
4380 }
4381 SuspensionTarget::None => true,
4382 };
4383
4384 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4385 return if yielding {
4386 Ok(WaitResult::Completed)
4387 } else {
4388 Err(Trap::CannotBlockSyncTask.into())
4389 };
4390 }
4391
4392 let guest_thread = store.current_guest_thread()?;
4393
4394 let reason = if yielding {
4395 SuspendReason::Yielding {
4396 thread: guest_thread,
4397 }
4398 } else {
4399 SuspendReason::ExplicitlySuspending {
4400 thread: guest_thread,
4401 }
4402 };
4403
4404 store.suspend(reason)?;
4405
4406 Ok(WaitResult::Completed)
4407 }
4408
4409 fn waitable_check(
4411 self,
4412 store: &mut StoreOpaque,
4413 caller: RuntimeInstance,
4414 check: WaitableCheck,
4415 params: WaitableCheckParams,
4416 ) -> Result<u32> {
4417 let guest_thread = store.current_guest_thread()?;
4418
4419 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4420
4421 match &check {
4424 WaitableCheck::Wait => {
4425 let set = params.set;
4426
4427 loop {
4432 let state = store.concurrent_state_mut()?;
4433 let task = state.get_mut(guest_thread.task)?;
4434 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4435 || !state.get_mut(set)?.ready.is_empty()
4436 {
4437 break;
4438 }
4439
4440 store.switch_or_trap_if_may_not_suspend(caller)?;
4441
4442 store.suspend(SuspendReason::Waiting {
4443 set,
4444 thread: guest_thread,
4445 })?;
4446 }
4447 }
4448 WaitableCheck::Poll => {}
4449 }
4450
4451 log::trace!(
4452 "waitable check for {guest_thread:?}; set {:?}, part two",
4453 params.set
4454 );
4455
4456 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4458
4459 let (ordinal, handle, result) = match &check {
4460 WaitableCheck::Wait => {
4461 let (event, waitable) = match event {
4462 Some(p) => p,
4463 None => bail_bug!("event expected to be present"),
4464 };
4465 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4466 let (ordinal, result) = event.parts();
4467 (ordinal, handle, result)
4468 }
4469 WaitableCheck::Poll => {
4470 if let Some((event, waitable)) = event {
4471 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4472 let (ordinal, result) = event.parts();
4473 (ordinal, handle, result)
4474 } else {
4475 log::trace!(
4476 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4477 guest_thread.task,
4478 params.set
4479 );
4480 let (ordinal, result) = Event::None.parts();
4481 (ordinal, 0, result)
4482 }
4483 }
4484 };
4485 let memory = self.options_memory_mut(store, params.options);
4486 let ptr = crate::component::func::validate_inbounds_dynamic(
4487 &CanonicalAbiInfo::POINTER_PAIR,
4488 memory,
4489 &ValRaw::u32(params.payload),
4490 )?;
4491 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4492 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4493 Ok(ordinal)
4494 }
4495
4496 pub(crate) fn subtask_cancel(
4498 self,
4499 store: &mut StoreOpaque,
4500 caller_instance: RuntimeComponentInstanceIndex,
4501 async_: bool,
4502 task_id: u32,
4503 ) -> Result<u32> {
4504 let (rep, is_host) = store
4505 .instance_state(self.runtime_instance(caller_instance))
4506 .handle_table()
4507 .subtask_rep(task_id)?;
4508 let waitable = if is_host {
4509 Waitable::Host(TableId::<HostTask>::new(rep))
4510 } else {
4511 Waitable::Guest(TableId::<GuestTask>::new(rep))
4512 };
4513 let concurrent_state = store.concurrent_state_mut()?;
4514
4515 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4516
4517 waitable.trap_if_in_waitable_set(concurrent_state)?;
4518
4519 let needs_block;
4520 if let Waitable::Host(host_task) = waitable {
4521 let state = &mut concurrent_state.get_mut(host_task)?.state;
4522 match state {
4523 HostTaskState::CalleeRunning(handle) => {
4530 handle.abort();
4531 *state = HostTaskState::CalleeCancelling;
4532 needs_block = true;
4533 }
4534
4535 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4538 bail!(Trap::SubtaskCancelAfterTerminal);
4539 }
4540 HostTaskState::CalleeDone { cancelled: false } => {
4541 *state = HostTaskState::CalleeDone { cancelled: true };
4544 needs_block = false;
4545 }
4546
4547 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4550 bail_bug!("invalid states for host callee")
4551 }
4552 }
4553 } else {
4554 let guest_task = TableId::<GuestTask>::new(rep);
4555 let task = concurrent_state.get_mut(guest_task)?;
4556
4557 if task.cancel_requested {
4560 bail!(Trap::SubtaskCancelAfterTerminal);
4561 }
4562
4563 if !task.already_lowered_parameters() {
4564 store.cancel_guest_subtask_without_lowered_parameters(
4565 self.runtime_instance(caller_instance),
4566 guest_task,
4567 )?;
4568 return Ok(Status::StartCancelled as u32);
4569 } else if !task.returned_or_cancelled() {
4570 task.cancel_requested = true;
4577 task.event = Some(Event::Cancelled);
4578 let runtime_instance = task.instance;
4579 for thread in task.threads.clone() {
4580 let thread = QualifiedThreadId {
4581 task: guest_task,
4582 thread,
4583 };
4584 let concurrent_state = store.concurrent_state_mut()?;
4585 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4586
4587 let yield_ = |store: &mut StoreOpaque| {
4588 let state = store.instance_state(runtime_instance).concurrent_state();
4593 let old_do_not_suspend = state.do_not_suspend;
4594 state.do_not_suspend = false;
4595
4596 let caller = store.current_guest_thread()?;
4597
4598 let state = store.concurrent_state_mut()?;
4603 let set = state.get_mut(caller.thread)?.sync_call_set;
4604 waitable.join(state, Some(set))?;
4605
4606 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4607
4608 let state = store.concurrent_state_mut()?;
4609 waitable.join(state, None)?;
4610
4611 store
4612 .instance_state(runtime_instance)
4613 .concurrent_state()
4614 .do_not_suspend = old_do_not_suspend;
4615
4616 Ok::<(), crate::Error>(())
4617 };
4618
4619 match thread_mut.wake_on_cancel.take() {
4620 WakeOnCancel::Waiting(set) => {
4621 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4623 let item = WorkItem::GuestCall {
4624 instance: runtime_instance,
4625 call: GuestCall {
4626 thread,
4627 kind: GuestCallKind::DeliverEvent {
4628 instance,
4629 set: Some(set),
4630 },
4631 },
4632 };
4633 concurrent_state.set_switch_item(item)?;
4634
4635 yield_(store)?;
4636
4637 break;
4638 }
4639 WakeOnCancel::Yielding => {
4640 if concurrent_state.promote_thread_work_item(thread)? {
4641 yield_(store)?;
4642 break;
4643 } else if store
4644 .instance_state(runtime_instance)
4645 .concurrent_state()
4646 .pending
4647 .contains_key(&thread)
4648 {
4649 store
4659 .concurrent_state_mut()?
4660 .get_mut(thread.thread)?
4661 .wake_on_cancel = WakeOnCancel::Yielding;
4662 } else {
4663 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4664 }
4665 }
4666 WakeOnCancel::None => {}
4667 }
4668 }
4669
4670 needs_block = !store
4673 .concurrent_state_mut()?
4674 .get_mut(guest_task)?
4675 .returned_or_cancelled()
4676 } else {
4677 needs_block = false;
4678 }
4679 };
4680
4681 if needs_block {
4685 if async_ {
4686 return Ok(BLOCKED);
4687 }
4688
4689 let old_next_switch_item = if is_host {
4699 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4700 } else {
4701 None
4702 };
4703
4704 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4707
4708 if let Some(item) = old_next_switch_item {
4709 let state = store.concurrent_state_mut()?;
4710 state.restore_next_switch_item(item)?;
4711 }
4712
4713 }
4715
4716 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4717 if let Some(Event::Subtask {
4718 status: status @ (Status::Returned | Status::ReturnCancelled),
4719 }) = event
4720 {
4721 Ok(status as u32)
4722 } else {
4723 bail!(Trap::SubtaskCancelAfterTerminal);
4724 }
4725 }
4726}
4727
4728pub trait VMComponentAsyncStore {
4736 unsafe fn prepare_call(
4742 &mut self,
4743 instance: Instance,
4744 memory: *mut VMMemoryDefinition,
4745 start: NonNull<VMFuncRef>,
4746 return_: NonNull<VMFuncRef>,
4747 caller_instance: RuntimeComponentInstanceIndex,
4748 callee_instance: RuntimeComponentInstanceIndex,
4749 task_return_type: TypeTupleIndex,
4750 callee_async: bool,
4751 string_encoding: StringEncoding,
4752 result_count: u32,
4753 storage: *mut ValRaw,
4754 storage_len: usize,
4755 ) -> Result<()>;
4756
4757 unsafe fn start_call(
4760 &mut self,
4761 instance: Instance,
4762 callback: *mut VMFuncRef,
4763 post_return: *mut VMFuncRef,
4764 callee: NonNull<VMFuncRef>,
4765 param_count: u32,
4766 result_count: u32,
4767 flags: u32,
4768 storage: *mut MaybeUninit<ValRaw>,
4769 storage_len: usize,
4770 ) -> Result<()>;
4771
4772 fn future_write(
4774 &mut self,
4775 instance: Instance,
4776 caller: RuntimeComponentInstanceIndex,
4777 ty: TypeFutureTableIndex,
4778 options: OptionsIndex,
4779 future: u32,
4780 address: u32,
4781 ) -> Result<u32>;
4782
4783 fn future_read(
4785 &mut self,
4786 instance: Instance,
4787 caller: RuntimeComponentInstanceIndex,
4788 ty: TypeFutureTableIndex,
4789 options: OptionsIndex,
4790 future: u32,
4791 address: u32,
4792 ) -> Result<u32>;
4793
4794 fn future_drop_writable(
4796 &mut self,
4797 instance: Instance,
4798 ty: TypeFutureTableIndex,
4799 writer: u32,
4800 ) -> Result<()>;
4801
4802 fn stream_write(
4804 &mut self,
4805 instance: Instance,
4806 caller: RuntimeComponentInstanceIndex,
4807 ty: TypeStreamTableIndex,
4808 options: OptionsIndex,
4809 stream: u32,
4810 address: u32,
4811 count: u32,
4812 ) -> Result<u32>;
4813
4814 fn stream_read(
4816 &mut self,
4817 instance: Instance,
4818 caller: RuntimeComponentInstanceIndex,
4819 ty: TypeStreamTableIndex,
4820 options: OptionsIndex,
4821 stream: u32,
4822 address: u32,
4823 count: u32,
4824 ) -> Result<u32>;
4825
4826 fn flat_stream_write(
4829 &mut self,
4830 instance: Instance,
4831 caller: RuntimeComponentInstanceIndex,
4832 ty: TypeStreamTableIndex,
4833 options: OptionsIndex,
4834 payload_size: u32,
4835 payload_align: u32,
4836 stream: u32,
4837 address: u32,
4838 count: u32,
4839 ) -> Result<u32>;
4840
4841 fn flat_stream_read(
4844 &mut self,
4845 instance: Instance,
4846 caller: RuntimeComponentInstanceIndex,
4847 ty: TypeStreamTableIndex,
4848 options: OptionsIndex,
4849 payload_size: u32,
4850 payload_align: u32,
4851 stream: u32,
4852 address: u32,
4853 count: u32,
4854 ) -> Result<u32>;
4855
4856 fn stream_drop_writable(
4858 &mut self,
4859 instance: Instance,
4860 ty: TypeStreamTableIndex,
4861 writer: u32,
4862 ) -> Result<()>;
4863
4864 fn error_context_debug_message(
4866 &mut self,
4867 instance: Instance,
4868 ty: TypeComponentLocalErrorContextTableIndex,
4869 options: OptionsIndex,
4870 err_ctx_handle: u32,
4871 debug_msg_address: u32,
4872 ) -> Result<()>;
4873
4874 fn thread_new_indirect(
4876 &mut self,
4877 instance: Instance,
4878 caller: RuntimeComponentInstanceIndex,
4879 func_ty_idx: ModuleInternedTypeIndex,
4880 start_func_table_idx: RuntimeTableIndex,
4881 start_func_idx: u32,
4882 context: i32,
4883 ) -> Result<u32>;
4884}
4885
4886impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4888 unsafe fn prepare_call(
4889 &mut self,
4890 instance: Instance,
4891 memory: *mut VMMemoryDefinition,
4892 start: NonNull<VMFuncRef>,
4893 return_: NonNull<VMFuncRef>,
4894 caller_instance: RuntimeComponentInstanceIndex,
4895 callee_instance: RuntimeComponentInstanceIndex,
4896 task_return_type: TypeTupleIndex,
4897 callee_async: bool,
4898 string_encoding: StringEncoding,
4899 result_count_or_max_if_async: u32,
4900 storage: *mut ValRaw,
4901 storage_len: usize,
4902 ) -> Result<()> {
4903 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4907
4908 unsafe {
4909 instance.prepare_call(
4910 StoreContextMut(self),
4911 start,
4912 return_,
4913 caller_instance,
4914 callee_instance,
4915 task_return_type,
4916 callee_async,
4917 memory,
4918 string_encoding,
4919 match result_count_or_max_if_async {
4920 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4921 params,
4922 has_result: false,
4923 },
4924 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4925 params,
4926 has_result: true,
4927 },
4928 result_count => CallerInfo::Sync {
4929 params,
4930 result_count,
4931 },
4932 },
4933 )
4934 }
4935 }
4936
4937 unsafe fn start_call(
4938 &mut self,
4939 instance: Instance,
4940 callback: *mut VMFuncRef,
4941 post_return: *mut VMFuncRef,
4942 callee: NonNull<VMFuncRef>,
4943 param_count: u32,
4944 result_count: u32,
4945 flags: u32,
4946 storage: *mut MaybeUninit<ValRaw>,
4947 storage_len: usize,
4948 ) -> Result<()> {
4949 unsafe {
4950 instance.start_call(
4951 StoreContextMut(self),
4952 callback,
4953 post_return,
4954 callee,
4955 param_count,
4956 result_count,
4957 flags,
4958 core::slice::from_raw_parts_mut(storage, storage_len),
4962 )
4963 }
4964 }
4965
4966 fn future_write(
4967 &mut self,
4968 instance: Instance,
4969 caller: RuntimeComponentInstanceIndex,
4970 ty: TypeFutureTableIndex,
4971 options: OptionsIndex,
4972 future: u32,
4973 address: u32,
4974 ) -> Result<u32> {
4975 instance
4976 .guest_write(
4977 StoreContextMut(self),
4978 caller,
4979 TransmitIndex::Future(ty),
4980 options,
4981 None,
4982 future,
4983 address,
4984 1,
4985 )
4986 .map(|result| result.encode())
4987 }
4988
4989 fn future_read(
4990 &mut self,
4991 instance: Instance,
4992 caller: RuntimeComponentInstanceIndex,
4993 ty: TypeFutureTableIndex,
4994 options: OptionsIndex,
4995 future: u32,
4996 address: u32,
4997 ) -> Result<u32> {
4998 instance
4999 .guest_read(
5000 StoreContextMut(self),
5001 caller,
5002 TransmitIndex::Future(ty),
5003 options,
5004 None,
5005 future,
5006 address,
5007 1,
5008 )
5009 .map(|result| result.encode())
5010 }
5011
5012 fn stream_write(
5013 &mut self,
5014 instance: Instance,
5015 caller: RuntimeComponentInstanceIndex,
5016 ty: TypeStreamTableIndex,
5017 options: OptionsIndex,
5018 stream: u32,
5019 address: u32,
5020 count: u32,
5021 ) -> Result<u32> {
5022 instance
5023 .guest_write(
5024 StoreContextMut(self),
5025 caller,
5026 TransmitIndex::Stream(ty),
5027 options,
5028 None,
5029 stream,
5030 address,
5031 count,
5032 )
5033 .map(|result| result.encode())
5034 }
5035
5036 fn stream_read(
5037 &mut self,
5038 instance: Instance,
5039 caller: RuntimeComponentInstanceIndex,
5040 ty: TypeStreamTableIndex,
5041 options: OptionsIndex,
5042 stream: u32,
5043 address: u32,
5044 count: u32,
5045 ) -> Result<u32> {
5046 instance
5047 .guest_read(
5048 StoreContextMut(self),
5049 caller,
5050 TransmitIndex::Stream(ty),
5051 options,
5052 None,
5053 stream,
5054 address,
5055 count,
5056 )
5057 .map(|result| result.encode())
5058 }
5059
5060 fn future_drop_writable(
5061 &mut self,
5062 instance: Instance,
5063 ty: TypeFutureTableIndex,
5064 writer: u32,
5065 ) -> Result<()> {
5066 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5067 }
5068
5069 fn flat_stream_write(
5070 &mut self,
5071 instance: Instance,
5072 caller: RuntimeComponentInstanceIndex,
5073 ty: TypeStreamTableIndex,
5074 options: OptionsIndex,
5075 payload_size: u32,
5076 payload_align: u32,
5077 stream: u32,
5078 address: u32,
5079 count: u32,
5080 ) -> Result<u32> {
5081 instance
5082 .guest_write(
5083 StoreContextMut(self),
5084 caller,
5085 TransmitIndex::Stream(ty),
5086 options,
5087 Some(FlatAbi {
5088 size: payload_size,
5089 align: payload_align,
5090 }),
5091 stream,
5092 address,
5093 count,
5094 )
5095 .map(|result| result.encode())
5096 }
5097
5098 fn flat_stream_read(
5099 &mut self,
5100 instance: Instance,
5101 caller: RuntimeComponentInstanceIndex,
5102 ty: TypeStreamTableIndex,
5103 options: OptionsIndex,
5104 payload_size: u32,
5105 payload_align: u32,
5106 stream: u32,
5107 address: u32,
5108 count: u32,
5109 ) -> Result<u32> {
5110 instance
5111 .guest_read(
5112 StoreContextMut(self),
5113 caller,
5114 TransmitIndex::Stream(ty),
5115 options,
5116 Some(FlatAbi {
5117 size: payload_size,
5118 align: payload_align,
5119 }),
5120 stream,
5121 address,
5122 count,
5123 )
5124 .map(|result| result.encode())
5125 }
5126
5127 fn stream_drop_writable(
5128 &mut self,
5129 instance: Instance,
5130 ty: TypeStreamTableIndex,
5131 writer: u32,
5132 ) -> Result<()> {
5133 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5134 }
5135
5136 fn error_context_debug_message(
5137 &mut self,
5138 instance: Instance,
5139 ty: TypeComponentLocalErrorContextTableIndex,
5140 options: OptionsIndex,
5141 err_ctx_handle: u32,
5142 debug_msg_address: u32,
5143 ) -> Result<()> {
5144 instance.error_context_debug_message(
5145 StoreContextMut(self),
5146 ty,
5147 options,
5148 err_ctx_handle,
5149 debug_msg_address,
5150 )
5151 }
5152
5153 fn thread_new_indirect(
5154 &mut self,
5155 instance: Instance,
5156 caller: RuntimeComponentInstanceIndex,
5157 func_ty_idx: ModuleInternedTypeIndex,
5158 start_func_table_idx: RuntimeTableIndex,
5159 start_func_idx: u32,
5160 context: i32,
5161 ) -> Result<u32> {
5162 instance.thread_new_indirect(
5163 StoreContextMut(self),
5164 caller,
5165 func_ty_idx,
5166 start_func_table_idx,
5167 start_func_idx,
5168 context,
5169 )
5170 }
5171}
5172
5173type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5174
5175async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5178where
5179 F: Future,
5180{
5181 let mut future = pin!(future);
5182 future::poll_fn(|cx| {
5183 let old_thread = match tls::get(|store| store.set_thread(task)) {
5184 Ok(thread) => thread,
5185 Err(error) => return Poll::Ready(Err(error)),
5186 };
5187 let result = future.as_mut().poll(cx);
5188 match tls::get(|store| store.set_thread(old_thread)) {
5189 Ok(_) => result.map(Ok),
5190 Err(error) => Poll::Ready(Err(error)),
5191 }
5192 })
5193 .await
5194}
5195
5196pub(crate) struct HostTask {
5200 common: WaitableCommon,
5201
5202 call_context: CallContext,
5205
5206 state: HostTaskState,
5207
5208 group: TaskGroupId,
5209}
5210
5211enum HostTaskState {
5212 CalleeStarted,
5217
5218 CalleeRunning(JoinHandle),
5223
5224 CalleeCancelling,
5228
5229 CalleeFinished(LiftedResult),
5233
5234 CalleeDone { cancelled: bool },
5237}
5238
5239impl HostTask {
5240 fn new(
5241 concurrent_state: &mut ConcurrentState,
5242 state: HostTaskState,
5243 caller: QualifiedThreadId,
5244 ) -> Result<Self> {
5245 let group = concurrent_state.get_mut(caller.task)?.group;
5246 concurrent_state.increment_group_ref_count(group)?;
5247
5248 Ok(Self {
5249 common: WaitableCommon::default(),
5250 call_context: CallContext::default(),
5251 state,
5252 group,
5253 })
5254 }
5255}
5256
5257impl TableDebug for HostTask {
5258 fn type_name() -> &'static str {
5259 "HostTask"
5260 }
5261}
5262
5263type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5264
5265enum Caller {
5267 Host {
5269 tx: Option<oneshot::Sender<LiftedResult>>,
5271 caller: Option<TableId<HostTask>>,
5275 },
5276 Guest {
5278 thread: QualifiedThreadId,
5280 },
5281}
5282
5283struct LiftResult {
5286 lift: RawLift,
5287 ty: TypeTupleIndex,
5288 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5289 string_encoding: StringEncoding,
5290}
5291
5292#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5297pub(crate) struct QualifiedThreadId {
5298 task: TableId<GuestTask>,
5299 thread: TableId<GuestThread>,
5300}
5301
5302impl QualifiedThreadId {
5303 fn qualify(
5304 state: &mut ConcurrentState,
5305 thread: TableId<GuestThread>,
5306 ) -> Result<QualifiedThreadId> {
5307 Ok(QualifiedThreadId {
5308 task: state.get_mut(thread)?.parent_task,
5309 thread,
5310 })
5311 }
5312}
5313
5314impl fmt::Debug for QualifiedThreadId {
5315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5316 f.debug_tuple("QualifiedThreadId")
5317 .field(&self.task.rep())
5318 .field(&self.thread.rep())
5319 .finish()
5320 }
5321}
5322
5323enum GuestThreadState {
5324 NotStartedImplicit,
5325 NotStartedExplicit(
5326 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5327 ),
5328 Running,
5329 Suspended(StoreFiber<'static>),
5330 Ready {
5331 fiber: StoreFiber<'static>,
5332 },
5333 Completed,
5334}
5335
5336impl GuestThreadState {
5337 fn check_no_fiber(&self) -> Result<()> {
5338 match self {
5339 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5340 Self::NotStartedImplicit
5341 | Self::NotStartedExplicit(_)
5342 | Self::Running
5343 | Self::Completed => Ok(()),
5344 }
5345 }
5346}
5347
5348impl fmt::Debug for GuestThreadState {
5349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5350 match self {
5351 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5352 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5353 Self::Running => f.debug_tuple("Running").finish(),
5354 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5355 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5356 Self::Completed => f.debug_tuple("Completed").finish(),
5357 }
5358 }
5359}
5360
5361#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5362enum WakeOnCancel {
5363 None,
5364 Waiting(TableId<WaitableSet>),
5365 Yielding,
5366}
5367
5368impl WakeOnCancel {
5369 fn is_none(self) -> bool {
5370 matches!(self, WakeOnCancel::None)
5371 }
5372
5373 fn replace(&mut self, other: WakeOnCancel) -> Self {
5374 let old = *self;
5375 *self = other;
5376 old
5377 }
5378
5379 fn take(&mut self) -> Self {
5380 self.replace(WakeOnCancel::None)
5381 }
5382}
5383
5384pub struct GuestThread {
5385 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5388 parent_task: TableId<GuestTask>,
5390 wake_on_cancel: WakeOnCancel,
5393 state: GuestThreadState,
5395 instance_rep: Option<u32>,
5398 sync_call_set: TableId<WaitableSet>,
5400 old_do_not_suspend: Option<bool>,
5403}
5404
5405impl GuestThread {
5406 fn from_instance(
5409 state: Pin<&mut ComponentInstance>,
5410 caller_instance: RuntimeComponentInstanceIndex,
5411 guest_thread: u32,
5412 ) -> Result<TableId<Self>> {
5413 let rep = state.instance_states().0[caller_instance]
5414 .thread_handle_table()
5415 .guest_thread_rep(guest_thread)?;
5416 Ok(TableId::new(rep))
5417 }
5418
5419 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5420 let sync_call_set = state.push(WaitableSet {
5421 is_sync_call_set: true,
5422 ..WaitableSet::default()
5423 })?;
5424 Ok(Self {
5425 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5426 parent_task,
5427 wake_on_cancel: WakeOnCancel::None,
5428 state: GuestThreadState::NotStartedImplicit,
5429 instance_rep: None,
5430 sync_call_set,
5431 old_do_not_suspend: None,
5432 })
5433 }
5434
5435 fn new_explicit(
5436 state: &mut ConcurrentState,
5437 parent_task: TableId<GuestTask>,
5438 start_func: Box<
5439 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5440 >,
5441 ) -> Result<Self> {
5442 state.increment_task_ref_count(parent_task)?;
5443 let sync_call_set = state.push(WaitableSet {
5444 is_sync_call_set: true,
5445 ..WaitableSet::default()
5446 })?;
5447 Ok(Self {
5448 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5449 parent_task,
5450 wake_on_cancel: WakeOnCancel::None,
5451 state: GuestThreadState::NotStartedExplicit(start_func),
5452 instance_rep: None,
5453 sync_call_set,
5454 old_do_not_suspend: None,
5455 })
5456 }
5457}
5458
5459impl TableDebug for GuestThread {
5460 fn type_name() -> &'static str {
5461 "GuestThread"
5462 }
5463}
5464
5465enum SyncResult {
5466 NotProduced,
5467 Produced(Option<ValRaw>),
5468 Taken,
5469}
5470
5471impl SyncResult {
5472 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5473 Ok(match mem::replace(self, SyncResult::Taken) {
5474 SyncResult::NotProduced => None,
5475 SyncResult::Produced(val) => Some(val),
5476 SyncResult::Taken => {
5477 bail_bug!("attempted to take a synchronous result that was already taken")
5478 }
5479 })
5480 }
5481}
5482
5483pub(crate) struct GuestTask {
5485 common: WaitableCommon,
5487 lower_params: Option<RawLower>,
5489 lift_result: Option<LiftResult>,
5491 result: Option<LiftedResult>,
5494 callback: Option<CallbackFn>,
5497 caller: Caller,
5499 call_context: CallContext,
5504 sync_result: SyncResult,
5507 cancel_request_delivered: bool,
5511 cancel_requested: bool,
5516 starting_sent: bool,
5519 instance: RuntimeInstance,
5526 event: Option<Event>,
5528 exited: bool,
5530 threads: HashSet<TableId<GuestThread>>,
5532 async_typed: bool,
5535 async_lifted: bool,
5538 decremented_interesting_task_count: bool,
5541 group: TaskGroupId,
5543 ref_count: usize,
5548}
5549
5550impl GuestTask {
5551 fn already_lowered_parameters(&self) -> bool {
5552 self.lower_params.is_none()
5554 }
5555
5556 fn returned_or_cancelled(&self) -> bool {
5557 self.lift_result.is_none()
5559 }
5560
5561 fn new(
5562 state: &mut ConcurrentState,
5563 lower_params: RawLower,
5564 lift_result: LiftResult,
5565 caller: Caller,
5566 callback: Option<CallbackFn>,
5567 instance: RuntimeInstance,
5568 async_typed: bool,
5569 async_lifted: bool,
5570 ) -> Result<QualifiedThreadId> {
5571 let group = match caller {
5572 Caller::Guest { thread } => {
5573 let group = state.get_mut(thread.task)?.group;
5574 state.increment_group_ref_count(group)?;
5575 group
5576 }
5577 Caller::Host { .. } => state.make_task_group()?,
5578 };
5579
5580 let task = state.push(Self {
5581 common: WaitableCommon::default(),
5582 lower_params: Some(lower_params),
5583 lift_result: Some(lift_result),
5584 result: None,
5585 callback,
5586 caller,
5587 call_context: CallContext::default(),
5588 sync_result: SyncResult::NotProduced,
5589 cancel_request_delivered: false,
5590 cancel_requested: false,
5591 starting_sent: false,
5592 instance,
5593 event: None,
5594 exited: false,
5595 threads: HashSet::new(),
5596 async_typed,
5597 async_lifted,
5598 decremented_interesting_task_count: false,
5599 group,
5600 ref_count: 2,
5603 })?;
5604 let new_thread = GuestThread::new_implicit(state, task)?;
5605 let thread = state.push(new_thread)?;
5606 state.get_mut(task)?.threads.insert(thread);
5607 state.interesting_tasks += 1;
5608 let thread = QualifiedThreadId { task, thread };
5609 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5610 Ok(thread)
5611 }
5612}
5613
5614impl TableDebug for GuestTask {
5615 fn type_name() -> &'static str {
5616 "GuestTask"
5617 }
5618}
5619
5620#[derive(Default)]
5622struct WaitableCommon {
5623 event: Option<Event>,
5625 set: Option<TableId<WaitableSet>>,
5627 handle: Option<u32>,
5629}
5630
5631#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5633enum Waitable {
5634 Host(TableId<HostTask>),
5636 Guest(TableId<GuestTask>),
5638 Transmit(TableId<TransmitHandle>),
5640}
5641
5642impl Waitable {
5643 fn from_instance(
5646 state: Pin<&mut ComponentInstance>,
5647 caller_instance: RuntimeComponentInstanceIndex,
5648 waitable: u32,
5649 ) -> Result<Self> {
5650 use crate::runtime::vm::component::Waitable;
5651
5652 let (waitable, kind) = state.instance_states().0[caller_instance]
5653 .handle_table()
5654 .waitable_rep(waitable)?;
5655
5656 Ok(match kind {
5657 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5658 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5659 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5660 })
5661 }
5662
5663 fn rep(&self) -> u32 {
5665 match self {
5666 Self::Host(id) => id.rep(),
5667 Self::Guest(id) => id.rep(),
5668 Self::Transmit(id) => id.rep(),
5669 }
5670 }
5671
5672 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5676 log::trace!("waitable {self:?} join set {set:?}");
5677
5678 let old = mem::replace(&mut self.common(state)?.set, set);
5679
5680 if let Some(old) = old {
5681 match *self {
5682 Waitable::Host(id) => state.remove_child(id, old),
5683 Waitable::Guest(id) => state.remove_child(id, old),
5684 Waitable::Transmit(id) => state.remove_child(id, old),
5685 }?;
5686
5687 state.get_mut(old)?.ready.remove(self);
5688 }
5689
5690 if let Some(set) = set {
5691 match *self {
5692 Waitable::Host(id) => state.add_child(id, set),
5693 Waitable::Guest(id) => state.add_child(id, set),
5694 Waitable::Transmit(id) => state.add_child(id, set),
5695 }?;
5696
5697 if self.common(state)?.event.is_some() {
5698 self.mark_ready(state)?;
5699 }
5700 }
5701
5702 Ok(())
5703 }
5704
5705 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5707 Ok(match self {
5708 Self::Host(id) => &mut state.get_mut(*id)?.common,
5709 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5710 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5711 })
5712 }
5713
5714 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5720 if self.common(state)?.set.is_some() {
5721 bail!(Trap::WaitableSyncAndAsync);
5722 }
5723 Ok(())
5724 }
5725
5726 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5732 if let Some(set) = self.common(state)?.set
5733 && state.get_mut(set)?.is_sync_call_set
5734 {
5735 bail!(Trap::WaitableSyncAndAsync);
5736 }
5737 Ok(())
5738 }
5739
5740 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5744 log::trace!("set event for {self:?}: {event:?}");
5745 self.common(state)?.event = event;
5746 self.mark_ready(state)
5747 }
5748
5749 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5751 let common = self.common(state)?;
5752 let event = common.event.take();
5753 if let Some(set) = self.common(state)?.set {
5754 state.get_mut(set)?.ready.remove(self);
5755 }
5756
5757 Ok(event)
5758 }
5759
5760 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5764 if let Some(set) = self.common(state)?.set {
5765 state.get_mut(set)?.ready.insert(*self);
5766 state.wake_waiter(set)?;
5767 }
5768 Ok(())
5769 }
5770
5771 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5774 match self {
5775 Self::Host(task) => state.delete_host_task(*task),
5776 Self::Guest(task) => state.decrement_task_ref_count(*task),
5777 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5778 }
5779 }
5780}
5781
5782impl fmt::Debug for Waitable {
5783 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5784 match self {
5785 Self::Host(id) => write!(f, "{id:?}"),
5786 Self::Guest(id) => write!(f, "{id:?}"),
5787 Self::Transmit(id) => write!(f, "{id:?}"),
5788 }
5789 }
5790}
5791
5792#[derive(Default)]
5794struct WaitableSet {
5795 ready: BTreeSet<Waitable>,
5797 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5799 num_waiting: usize,
5802 is_sync_call_set: bool,
5805}
5806
5807impl WaitableSet {
5808 fn stop_waiting(&mut self) -> Result<()> {
5810 self.num_waiting = match self.num_waiting.checked_sub(1) {
5811 Some(n) => n,
5812 None => bail_bug!("waiter not accounted for in waitable set"),
5813 };
5814 Ok(())
5815 }
5816}
5817
5818impl TableDebug for WaitableSet {
5819 fn type_name() -> &'static str {
5820 "WaitableSet"
5821 }
5822}
5823
5824type RawLower =
5826 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5827
5828type RawLift = Box<
5830 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5831>;
5832
5833type LiftedResult = Box<dyn Any + Send + Sync>;
5837
5838struct DummyResult;
5841
5842#[derive(Default)]
5844pub struct ConcurrentInstanceState {
5845 backpressure: u16,
5847 do_not_enter: bool,
5849 do_not_suspend: bool,
5852 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5855}
5856
5857impl ConcurrentInstanceState {
5858 pub fn pending_is_empty(&self) -> bool {
5859 self.pending.is_empty()
5860 }
5861}
5862
5863#[derive(Debug, Copy, Clone)]
5864pub(crate) enum CurrentThread {
5865 Guest(QualifiedThreadId),
5868 Host(TableId<HostTask>),
5870 DeferredHost(QualifiedThreadId),
5873 None,
5876}
5877
5878impl CurrentThread {
5879 fn guest(&self) -> Option<&QualifiedThreadId> {
5880 match self {
5881 Self::Guest(id) => Some(id),
5882 _ => None,
5883 }
5884 }
5885
5886 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5887 match self {
5888 Self::Guest(id) => Some(id.task),
5889 _ => None,
5890 }
5891 }
5892
5893 fn is_none(&self) -> bool {
5894 matches!(self, Self::None)
5895 }
5896}
5897
5898impl From<QualifiedThreadId> for CurrentThread {
5899 fn from(id: QualifiedThreadId) -> Self {
5900 Self::Guest(id)
5901 }
5902}
5903
5904impl From<TableId<HostTask>> for CurrentThread {
5905 fn from(id: TableId<HostTask>) -> Self {
5906 Self::Host(id)
5907 }
5908}
5909
5910enum Priority {
5911 Switch,
5912 High,
5913 Low,
5914}
5915
5916pub struct ConcurrentState {
5918 unforced_current_thread: CurrentThread,
5924
5925 deferred_host_call_context: Option<CallContext>,
5931
5932 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5937 table: AlwaysMut<ResourceTable>,
5939 switch_item: Option<WorkItem>,
5947 next_switch_item: Option<WorkItem>,
5953 high_priority: VecDeque<WorkItem>,
5955 low_priority: VecDeque<WorkItem>,
5957 suspend_reason: Option<SuspendReason>,
5961 worker: Option<StoreFiber<'static>>,
5965 worker_item: Option<WorkerItem>,
5967
5968 global_error_context_ref_counts:
5981 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5982
5983 interesting_tasks: usize,
5996
5997 interesting_tasks_empty_waker: Option<Waker>,
6001
6002 ready_for_concurrent_call_waker: Option<Waker>,
6007
6008 event_loop_running: bool,
6010
6011 saved_next_switch_items: Vec<Option<WorkItem>>,
6014
6015 #[cfg(feature = "task-group-hook")]
6017 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6018}
6019
6020impl Default for ConcurrentState {
6021 fn default() -> Self {
6022 Self {
6023 unforced_current_thread: CurrentThread::None,
6024 deferred_host_call_context: None,
6025 table: AlwaysMut::new(ResourceTable::new()),
6026 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6027 switch_item: None,
6028 next_switch_item: None,
6029 high_priority: VecDeque::new(),
6030 low_priority: VecDeque::new(),
6031 suspend_reason: None,
6032 worker: None,
6033 worker_item: None,
6034 global_error_context_ref_counts: BTreeMap::new(),
6035 interesting_tasks: 0,
6036 interesting_tasks_empty_waker: None,
6037 ready_for_concurrent_call_waker: None,
6038 event_loop_running: false,
6039 saved_next_switch_items: Vec::new(),
6040 #[cfg(feature = "task-group-hook")]
6041 task_group_hook: None,
6042 }
6043 }
6044}
6045
6046impl ConcurrentState {
6047 pub(crate) fn take_fibers_and_futures(
6064 &mut self,
6065 fibers: &mut Vec<StoreFiber<'static>>,
6066 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6067 ) {
6068 let ConcurrentState {
6069 table,
6070 worker,
6071 switch_item,
6072 next_switch_item,
6073 high_priority,
6074 low_priority,
6075 saved_next_switch_items,
6076 futures: pending_futures,
6077 worker_item: _,
6078 unforced_current_thread: _,
6079 deferred_host_call_context: _,
6080 suspend_reason: _,
6081 global_error_context_ref_counts: _,
6082 interesting_tasks: _,
6083 interesting_tasks_empty_waker: _,
6084 ready_for_concurrent_call_waker: _,
6085 event_loop_running: _,
6086 #[cfg(feature = "task-group-hook")]
6087 task_group_hook: _,
6088 } = self;
6089
6090 let mut items = Vec::new();
6091 for (_, entry) in table.get_mut().iter_mut() {
6092 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6093 for mode in mem::take(&mut set.waiting).into_values() {
6094 match mode {
6095 WaitMode::Fiber(fiber) => {
6096 fibers.push(fiber);
6097 }
6098 WaitMode::Callback(_) => {}
6099 }
6100 }
6101 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6102 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6103 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6104 fibers.push(fiber);
6105 }
6106 GuestThreadState::NotStartedImplicit
6107 | GuestThreadState::NotStartedExplicit(_)
6108 | GuestThreadState::Running
6109 | GuestThreadState::Completed => {}
6110 }
6111 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6112 if let Some(item) = item.take() {
6113 items.push(item);
6114 }
6115 }
6116 }
6117
6118 if let Some(fiber) = worker.take() {
6119 fibers.push(fiber);
6120 }
6121
6122 let mut handle_item = |item| match item {
6123 WorkItem::ResumeFiber { fiber, .. } => {
6124 fibers.push(fiber);
6125 }
6126 WorkItem::PushFuture(future) => {
6127 pending_futures
6128 .get_mut()
6129 .as_mut()
6130 .unwrap()
6131 .push(future.into_inner());
6132 }
6133 WorkItem::ResumeThread { .. }
6134 | WorkItem::GuestCall { .. }
6135 | WorkItem::WorkerFunction(_) => {}
6136 };
6137
6138 for item in items {
6139 handle_item(item);
6140 }
6141 if let Some(item) = switch_item.take() {
6142 handle_item(item);
6143 }
6144 if let Some(item) = next_switch_item.take() {
6145 handle_item(item);
6146 }
6147 for item in mem::take(high_priority) {
6148 handle_item(item);
6149 }
6150 for item in mem::take(low_priority) {
6151 handle_item(item);
6152 }
6153 for item in mem::take(saved_next_switch_items)
6154 .into_iter()
6155 .filter_map(|v| v)
6156 {
6157 handle_item(item);
6158 }
6159
6160 if let Some(them) = pending_futures.get_mut().take() {
6161 futures.push(them);
6162 }
6163 }
6164
6165 #[cfg(feature = "gc")]
6166 pub(crate) fn trace_fiber_roots(
6167 &mut self,
6168 modules: &ModuleRegistry,
6169 unwind: &dyn Unwind,
6170 gc_roots_list: &mut GcRootsList,
6171 ) {
6172 let ConcurrentState {
6173 table,
6174 worker,
6175 switch_item,
6176 next_switch_item,
6177 high_priority,
6178 low_priority,
6179 saved_next_switch_items,
6180
6181 futures: _,
6185
6186 worker_item: _,
6188 unforced_current_thread: _,
6189 deferred_host_call_context: _,
6190 suspend_reason: _,
6191 global_error_context_ref_counts: _,
6192 interesting_tasks: _,
6193 interesting_tasks_empty_waker: _,
6194 ready_for_concurrent_call_waker: _,
6195 event_loop_running: _,
6196 #[cfg(feature = "task-group-hook")]
6197 task_group_hook: _,
6198 } = self;
6199
6200 for (_, entry) in table.get_mut().iter_mut() {
6201 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6202 for mode in set.waiting.values_mut() {
6203 match mode {
6204 WaitMode::Fiber(fiber) => {
6205 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6206 }
6207 WaitMode::Callback(_) => {}
6208 }
6209 }
6210 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6211 match &mut thread.state {
6212 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6213 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6214 }
6215 GuestThreadState::NotStartedImplicit
6216 | GuestThreadState::NotStartedExplicit(_)
6217 | GuestThreadState::Running
6218 | GuestThreadState::Completed => {}
6219 }
6220 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6221 entry.downcast_mut::<Option<WorkItem>>()
6222 {
6223 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6224 }
6225 }
6226
6227 if let Some(fiber) = worker {
6228 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6229 }
6230
6231 let mut handle_item = |item: &mut WorkItem| match item {
6232 WorkItem::ResumeFiber { fiber, .. } => {
6233 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6234 }
6235 WorkItem::PushFuture(_future) => {
6236 }
6239 WorkItem::ResumeThread { .. }
6240 | WorkItem::GuestCall { .. }
6241 | WorkItem::WorkerFunction(_) => {}
6242 };
6243
6244 if let Some(item) = switch_item {
6245 handle_item(item);
6246 }
6247 if let Some(item) = next_switch_item {
6248 handle_item(item);
6249 }
6250 for item in high_priority {
6251 handle_item(item);
6252 }
6253 for item in low_priority {
6254 handle_item(item);
6255 }
6256 for item in saved_next_switch_items
6257 .iter_mut()
6258 .filter_map(|v| v.as_mut())
6259 {
6260 handle_item(item);
6261 }
6262 }
6263
6264 fn schedule_suspended_thread(
6265 &mut self,
6266 thread: QualifiedThreadId,
6267 instance: RuntimeInstance,
6268 priority: Priority,
6269 ) -> Result<()> {
6270 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6272 bail_bug!("switch item already set");
6273 }
6274 let slot = &mut self.get_mut(thread.thread)?.state;
6275 if !matches!(slot, GuestThreadState::Suspended(_)) {
6276 bail!(Trap::CannotResumeThread);
6277 }
6278 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6279 else {
6280 unreachable!()
6281 };
6282 let item = WorkItem::ResumeFiber {
6283 instance,
6284 thread,
6285 fiber,
6286 };
6287 match priority {
6288 Priority::Switch => self.switch_item = Some(item),
6289 Priority::High => self.push_high_priority(item),
6290 Priority::Low => self.push_low_priority(item),
6291 }
6292 Ok(())
6293 }
6294
6295 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6296 let slot = &mut self.get_mut(thread)?.state;
6297 slot.check_no_fiber()?;
6298 *slot = GuestThreadState::Running;
6299 Ok(())
6300 }
6301
6302 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6303 let saved = self.push(None::<WorkItem>)?;
6306 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6307 *slot = self.next_switch_item.take();
6308 Ok(saved)
6309 }
6310
6311 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6312 if self.next_switch_item.is_some() {
6313 bail_bug!("next switch item already set when restoring");
6314 }
6315 self.next_switch_item = self.delete(saved)?;
6316 Ok(())
6317 }
6318
6319 fn take_callback_waiter(
6320 &mut self,
6321 set: TableId<WaitableSet>,
6322 thread: QualifiedThreadId,
6323 ) -> Result<Instance> {
6324 let waiting = &mut self.get_mut(set)?.waiting;
6325 let instance = match waiting.get(&thread) {
6326 Some(WaitMode::Callback(instance)) => *instance,
6327 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6328 };
6329 waiting.remove(&thread);
6330 Ok(instance)
6331 }
6332
6333 fn push<V: Send + Sync + 'static>(
6334 &mut self,
6335 value: V,
6336 ) -> Result<TableId<V>, ResourceTableError> {
6337 self.table.get_mut().push(value).map(TableId::from)
6338 }
6339
6340 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6341 self.table.get_mut().get_mut(&Resource::from(id))
6342 }
6343
6344 pub fn add_child<T: 'static, U: 'static>(
6345 &mut self,
6346 child: TableId<T>,
6347 parent: TableId<U>,
6348 ) -> Result<(), ResourceTableError> {
6349 self.table
6350 .get_mut()
6351 .add_child(Resource::from(child), Resource::from(parent))
6352 }
6353
6354 pub fn remove_child<T: 'static, U: 'static>(
6355 &mut self,
6356 child: TableId<T>,
6357 parent: TableId<U>,
6358 ) -> Result<(), ResourceTableError> {
6359 self.table
6360 .get_mut()
6361 .remove_child(Resource::from(child), Resource::from(parent))
6362 }
6363
6364 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6365 self.table.get_mut().delete(Resource::from(id))
6366 }
6367
6368 fn push_future(&mut self, future: HostTaskFuture) {
6369 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6376 }
6377
6378 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6379 if self.switch_item.is_some() {
6380 self.push_high_priority(item);
6383 bail_bug!("switch item already set");
6384 }
6385
6386 self.switch_item = Some(item);
6387 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6388
6389 Ok(())
6390 }
6391
6392 fn take_next_switch_item(&mut self) -> Result<()> {
6393 if let Some(item) = self.next_switch_item.take() {
6394 self.set_switch_item(item)?;
6395 }
6396 Ok(())
6397 }
6398
6399 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6402 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6405 return Ok(());
6406 };
6407 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6408 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6409 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6410 }
6411 let instance = self.get_mut(thread.task)?.instance;
6412 self.get_mut(thread.thread)?.wake_on_cancel.take();
6413 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6414
6415 let item = match mode {
6416 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6417 instance,
6418 thread,
6419 fiber,
6420 },
6421 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6422 instance,
6423 call: GuestCall {
6424 thread,
6425 kind: GuestCallKind::DeliverEvent {
6426 instance: callback_instance,
6427 set: Some(set),
6428 },
6429 },
6430 },
6431 };
6432 self.push_high_priority(item);
6433 Ok(())
6434 }
6435
6436 fn push_high_priority(&mut self, item: WorkItem) {
6437 self.high_priority.push_front(item);
6438 log::trace!(
6439 "push high priority: {:?}",
6440 self.high_priority.front().unwrap()
6441 );
6442 }
6443
6444 fn push_low_priority(&mut self, item: WorkItem) {
6445 self.low_priority.push_front(item);
6446 log::trace!(
6447 "push low priority: {:?}",
6448 self.low_priority.front().unwrap()
6449 );
6450 }
6451
6452 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6453 match priority {
6454 Priority::Switch => self.set_switch_item(item)?,
6455 Priority::High => self.push_high_priority(item),
6456 Priority::Low => self.push_low_priority(item),
6457 }
6458
6459 Ok(())
6460 }
6461
6462 fn promote_instance_local_thread_work_item(
6463 &mut self,
6464 current_instance: RuntimeInstance,
6465 ) -> Result<bool> {
6466 log::trace!("promote thread work items for {current_instance:?}");
6467
6468 self.promote_work_item_matching(|item: &WorkItem| {
6469 let result = match item {
6470 WorkItem::ResumeThread { instance, .. }
6471 | WorkItem::ResumeFiber { instance, .. }
6472 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6473 _ => false,
6474 };
6475
6476 log::trace!("candidate {item:?}: {result}");
6477 result
6478 })
6479 }
6480
6481 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6482 self.promote_work_item_matching(|item: &WorkItem| match item {
6483 WorkItem::ResumeThread {
6484 thread: item_thread,
6485 ..
6486 }
6487 | WorkItem::GuestCall {
6488 call:
6489 GuestCall {
6490 thread: item_thread,
6491 ..
6492 },
6493 ..
6494 } => *item_thread == thread,
6495 _ => false,
6496 })
6497 }
6498
6499 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6500 where
6501 F: FnMut(&WorkItem) -> bool,
6502 {
6503 if self.switch_item.is_none() {
6510 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6511 self.switch_item = self.high_priority.remove(index);
6512 }
6513 }
6514 if self.switch_item.is_none() {
6515 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6516 self.switch_item = self.low_priority.remove(index);
6517 }
6518 }
6519 Ok(self.switch_item.is_some())
6520 }
6521
6522 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6525 match task {
6526 Scope::HostId(task) => {
6527 let task: TableId<HostTask> = TableId::new(task);
6528 Ok(&mut self.get_mut(task)?.call_context)
6529 }
6530 Scope::Id(task) => {
6531 let task: TableId<GuestTask> = TableId::new(task);
6532 Ok(&mut self.get_mut(task)?.call_context)
6533 }
6534 }
6535 }
6536
6537 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6538 self.deferred_host_call_context.as_mut()
6539 }
6540
6541 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6542 match self.futures.get_mut().as_mut() {
6543 Some(f) => Ok(f),
6544 None => bail_bug!("futures field of concurrent state is currently taken"),
6545 }
6546 }
6547
6548 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6549 self.table.get_mut()
6550 }
6551
6552 fn debug_assert_deferred_host_invariant(&self) {
6553 debug_assert_eq!(
6554 self.deferred_host_call_context.is_some(),
6555 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6556 "a deferred host thread and call context must exist together",
6557 );
6558 }
6559
6560 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6561 self.debug_assert_deferred_host_invariant();
6562 let caller = match self.unforced_current_thread {
6563 CurrentThread::DeferredHost(caller) => caller,
6564 thread => return Ok(thread),
6565 };
6566
6567 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6569 let task = self.push(task)?;
6570 let call_context = self
6571 .deferred_host_call_context
6572 .take()
6573 .expect("deferred host call context should be present");
6574 self.get_mut(task)
6575 .expect("newly inserted host task should be present")
6576 .call_context = call_context;
6577 self.unforced_current_thread = CurrentThread::Host(task);
6578 self.debug_assert_deferred_host_invariant();
6579 log::trace!("new host task materialized {task:?}");
6580 Ok(CurrentThread::Host(task))
6581 }
6582
6583 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6584 match self.materialize_host_task()? {
6585 CurrentThread::Host(id) => Ok(Some(id)),
6586 CurrentThread::None => Ok(None),
6587 CurrentThread::Guest(_) => {
6588 bail_bug!("tried to materialize a host task id from a guest thread")
6589 }
6590 CurrentThread::DeferredHost(_) => {
6591 bail_bug!(
6592 "current thread is a deferred host thread which should have been materialized"
6593 )
6594 }
6595 }
6596 }
6597
6598 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6599 match self.materialize_host_task()? {
6600 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6601 _ => bail_bug!("current scope is not a deferred host scope"),
6602 }
6603 }
6604
6605 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6606 let count = &mut self.get_mut(task)?.ref_count;
6607 *count += 1;
6608 log::trace!("increment {task:?} to {count}");
6609 Ok(())
6610 }
6611
6612 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6613 let count = &mut self.get_mut(task)?.ref_count;
6614 if *count == 0 {
6615 bail_bug!("can't decrement task ref count that's already zero");
6616 }
6617 *count -= 1;
6618 log::trace!("decrement {task:?} to {count}");
6619 if *count == 0 {
6620 let task = self.delete(task)?;
6621
6622 self.decrement_group_ref_count(task.group)?;
6623
6624 debug_assert!(task.decremented_interesting_task_count);
6631 }
6632 Ok(())
6633 }
6634
6635 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6636 log::trace!("delete host task {task:?}");
6637 let task = self.delete(task)?;
6638
6639 self.decrement_group_ref_count(task.group)
6640 }
6641}
6642
6643fn for_any_lower<
6646 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6647>(
6648 fun: F,
6649) -> F {
6650 fun
6651}
6652
6653fn for_any_lift<
6655 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6656>(
6657 fun: F,
6658) -> F {
6659 fun
6660}
6661
6662fn check_ambient_store(id: StoreId) {
6663 let message = "\
6664 `Future`s which depend on asynchronous component tasks, streams, or \
6665 futures to complete may only be polled from the event loop of the \
6666 store to which they belong. Please use \
6667 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6668 ";
6669 tls::try_get(|store| {
6670 let matched = match store {
6671 tls::TryGet::Some(store) => store.id() == id,
6672 tls::TryGet::Taken | tls::TryGet::None => false,
6673 };
6674
6675 if !matched {
6676 panic!("{message}")
6677 }
6678 });
6679}
6680
6681fn unpack_callback_code(code: u32) -> (u32, u32) {
6682 (code & 0xF, code >> 4)
6683}
6684
6685struct WaitableCheckParams {
6689 set: TableId<WaitableSet>,
6690 options: OptionsIndex,
6691 payload: u32,
6692}
6693
6694enum WaitableCheck {
6697 Wait,
6698 Poll,
6699}
6700
6701pub(crate) struct PreparedCall<R> {
6703 handle: Func,
6705 thread: QualifiedThreadId,
6707 param_count: usize,
6709 rx: oneshot::Receiver<LiftedResult>,
6712 _phantom: PhantomData<R>,
6713}
6714
6715impl<R> PreparedCall<R> {
6716 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6723 store
6724 .concurrent_state_mut()?
6725 .increment_task_ref_count(self.thread.task)?;
6726
6727 Ok(TaskId {
6728 task: self.thread.task,
6729 })
6730 }
6731}
6732
6733pub(crate) struct TaskId {
6735 task: TableId<GuestTask>,
6736}
6737
6738impl TaskId {
6739 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6751 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6752 if !task.already_lowered_parameters() {
6753 task.lower_params = Some(Box::new(|store, _| {
6758 store.set_trapped();
6759 bail!("the host future for this call was dropped before it started")
6760 }));
6761 } else {
6762 _ = store
6767 .concurrent_state_mut()?
6768 .decrement_task_ref_count(self.task);
6769 }
6770
6771 Ok(())
6772 }
6773}
6774
6775pub(crate) fn prepare_call<T, R>(
6781 mut store: StoreContextMut<T>,
6782 handle: Func,
6783 param_count: usize,
6784 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6785 + Send
6786 + Sync
6787 + 'static,
6788 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6789 + Send
6790 + Sync
6791 + 'static,
6792) -> Result<PreparedCall<R>> {
6793 if !store.0.may_enter() {
6794 bail!(Trap::CannotEnterComponent);
6795 }
6796
6797 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6798
6799 let instance = handle.instance().id().get(store.0);
6800 let options = &instance.component().env_component().options[options];
6801 let ty = &instance.component().types()[ty];
6802 let async_typed = ty.async_;
6803 let async_lifted = raw_options.async_;
6804 let task_return_type = ty.results;
6805 let component_instance = raw_options.instance;
6806 let callback = options.callback.map(|i| instance.runtime_callback(i));
6807 let memory = options
6808 .memory()
6809 .map(|i| instance.runtime_memory(i))
6810 .map(SendSyncPtr::new);
6811 let string_encoding = options.string_encoding;
6812 let token = StoreToken::new(store.as_context_mut());
6813 let caller = store.0.materialize_host_task_id()?;
6814 let state = store.0.concurrent_state_mut()?;
6815
6816 let (tx, rx) = oneshot::channel();
6817
6818 let instance = handle.instance().runtime_instance(component_instance);
6819 let thread = GuestTask::new(
6820 state,
6821 Box::new(for_any_lower(move |store, params| {
6822 lower_params(token.as_context_mut(store), params)
6823 })),
6824 LiftResult {
6825 lift: Box::new(for_any_lift(move |store, result| {
6826 lift_result(store, result)
6827 })),
6828 ty: task_return_type,
6829 memory,
6830 string_encoding,
6831 },
6832 Caller::Host {
6833 tx: Some(tx),
6834 caller,
6835 },
6836 callback.map(|callback| {
6837 let callback = SendSyncPtr::new(callback);
6838 let instance = handle.instance();
6839 Box::new(move |store: &mut dyn VMStore, event, handle| {
6840 let store = token.as_context_mut(store);
6841 unsafe { instance.call_callback(store, callback, event, handle) }
6844 }) as CallbackFn
6845 }),
6846 instance,
6847 async_typed,
6848 async_lifted,
6849 )?;
6850
6851 Ok(PreparedCall {
6852 handle,
6853 thread,
6854 param_count,
6855 rx,
6856 _phantom: PhantomData,
6857 })
6858}
6859
6860pub(crate) struct StagedCall<R> {
6861 store: StoreId,
6862 rx: oneshot::Receiver<LiftedResult>,
6863 _marker: PhantomData<fn() -> R>,
6864 group: TaskGroupId,
6872}
6873
6874impl<R> StagedCall<R> {
6875 pub(crate) fn new<T: 'static>(
6886 mut store: StoreContextMut<T>,
6887 prepared: PreparedCall<R>,
6888 ) -> Result<StagedCall<R>> {
6889 let PreparedCall {
6890 handle,
6891 thread,
6892 param_count,
6893 rx,
6894 ..
6895 } = prepared;
6896
6897 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6898
6899 let state = store.0.concurrent_state_mut()?;
6900 let group = state.get_mut(thread.task)?.group;
6901 state.decrement_task_ref_count(thread.task)?;
6902
6903 Ok(StagedCall {
6904 store: store.0.id(),
6905 rx,
6906 _marker: PhantomData,
6907 group,
6908 })
6909 }
6910}
6911
6912impl<R> Future for StagedCall<R>
6913where
6914 R: 'static,
6915{
6916 type Output = Result<R>;
6917
6918 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6919 check_ambient_store(self.store);
6920 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6921 Ok(r) => match r.downcast() {
6922 Ok(r) => Ok(*r),
6923 Err(_) => bail_bug!("wrong type of value produced"),
6924 },
6925 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6926 })
6927 }
6928}
6929
6930fn stage_call0<T: 'static>(
6933 store: StoreContextMut<T>,
6934 handle: Func,
6935 guest_thread: QualifiedThreadId,
6936 param_count: usize,
6937) -> Result<()> {
6938 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6939 let is_concurrent = raw_options.async_;
6940 let callback = raw_options.callback;
6941 let instance = handle.instance();
6942 let callee = handle.lifted_core_func(store.0);
6943 let post_return = raw_options
6944 .post_return
6945 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6946 let callback = callback.map(|i| {
6947 let instance = instance.id().get(store.0);
6948 SendSyncPtr::new(instance.runtime_callback(i))
6949 });
6950
6951 log::trace!("queueing call {guest_thread:?}");
6952
6953 unsafe {
6957 instance.stage_call(
6958 store,
6959 guest_thread,
6960 SendSyncPtr::new(callee),
6961 param_count,
6962 1,
6963 is_concurrent,
6964 callback,
6965 post_return.map(SendSyncPtr::new),
6966 true,
6967 )
6968 }
6969}
6970
6971#[cfg(all(test, feature = "cranelift", feature = "wat"))]
6972mod tests {
6973 use super::*;
6974 use crate::component::{Component, Linker};
6975 use crate::store::AsStoreOpaque;
6976 use crate::{Config, Engine};
6977
6978 fn host_subtask(
6979 state: HostTaskState,
6980 event: Option<Event>,
6981 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
6982 let mut config = Config::new();
6983 config.wasm_component_model_async(true);
6984 let engine = Engine::new(&config)?;
6985 let component = Component::new(&engine, "(component)")?;
6986 let mut store = Store::new(&engine, ());
6987 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
6988 let store_opaque = store.as_store_opaque();
6989 let concurrent_state = store_opaque.concurrent_state_mut()?;
6990 let group = concurrent_state.make_task_group()?;
6993 let task = concurrent_state.push(HostTask {
6994 common: WaitableCommon::default(),
6995 call_context: CallContext::default(),
6996 state,
6997 group,
6998 })?;
6999 let (state, counter) = store_opaque.instance_state_and_handle_counter(
7000 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
7001 );
7002 let handle = state
7003 .handle_table()
7004 .subtask_insert_host(counter, task.rep())?;
7005 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7006 common.handle = Some(handle);
7007 common.event = event;
7008 Ok((store, instance, task, handle))
7009 }
7010
7011 #[test]
7012 fn host_subtask_drop_during_cancellation() -> Result<()> {
7013 for abort_completed in [false, true] {
7014 let (handle, future) = JoinHandle::run(future::pending::<()>());
7015 let mut future = pin!(future);
7016 let (mut store, instance, task, handle) =
7017 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7018 let store = store.as_store_opaque();
7019 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7020 assert_eq!(
7021 instance.subtask_cancel(store, caller, true, handle)?,
7022 BLOCKED
7023 );
7024 if abort_completed {
7025 assert!(matches!(
7028 future
7029 .as_mut()
7030 .poll(&mut Context::from_waker(Waker::noop())),
7031 Poll::Ready(None),
7032 ));
7033 }
7034 for async_ in [false, true] {
7035 let err = instance
7036 .subtask_cancel(store, caller, async_, handle)
7037 .unwrap_err();
7038 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7039 }
7040 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7041 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7042 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7043 }
7044 Ok(())
7045 }
7046
7047 #[test]
7048 fn host_subtask_cancel_after_completion() -> Result<()> {
7049 for async_ in [false, true] {
7050 let (mut store, instance, task, handle) = host_subtask(
7051 HostTaskState::CalleeDone { cancelled: false },
7052 Some(Event::Subtask {
7053 status: Status::Returned,
7054 }),
7055 )?;
7056 let store = store.as_store_opaque();
7057 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7058 assert_eq!(
7059 instance.subtask_cancel(store, caller, async_, handle)?,
7060 Status::Returned as u32,
7061 );
7062 let err = instance
7063 .subtask_cancel(store, caller, async_, handle)
7064 .unwrap_err();
7065 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7066 instance.subtask_drop(store, caller, handle)?;
7067 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7068 }
7069 Ok(())
7070 }
7071
7072 #[test]
7073 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7074 for (cancelled, status) in [
7075 (false, Status::Returned),
7076 (true, Status::Returned),
7077 (true, Status::ReturnCancelled),
7078 ] {
7079 for delivered in [false, true] {
7080 let event = if delivered {
7081 None
7082 } else {
7083 Some(Event::Subtask { status })
7084 };
7085 let (mut store, instance, task, handle) =
7086 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7087 let store = store.as_store_opaque();
7088 let result = instance.subtask_drop(
7089 store,
7090 RuntimeComponentInstanceIndex::from_u32(0),
7091 handle,
7092 );
7093 if delivered {
7094 result?;
7095 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7096 } else {
7097 let err = result.unwrap_err();
7098 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7099 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7100 }
7101 }
7102 }
7103 Ok(())
7104 }
7105}