1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::{
56 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
57};
58use crate::fiber::{self, StoreFiber, StoreFiberYield};
59use crate::hash_set::HashSet;
60#[cfg(feature = "gc")]
61use crate::module::ModuleRegistry;
62use crate::prelude::*;
63use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
64#[cfg(feature = "gc")]
65use crate::vm::GcRootsList;
66use crate::vm::component::{
67 CallContext, ComponentInstance, CurrentScope, HandleCounter, InstanceState, Scope,
68};
69use crate::vm::{
70 AlwaysMut, SendSyncPtr, UncaughtException, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore,
71};
72use crate::{AsContext, AsContextMut, Result, StoreContext, StoreContextMut, ValRaw, bail};
73use crate::{Instance as ModuleInstance, bail_bug};
74use alloc::borrow::ToOwned;
75use alloc::collections::btree_map::Entry;
76use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
77use core::any::Any;
78use core::cell::UnsafeCell;
79use core::fmt;
80use core::future;
81use core::future::Future;
82use core::marker::PhantomData;
83use core::mem::{self, ManuallyDrop, MaybeUninit};
84use core::ops::DerefMut;
85use core::pin::{Pin, pin};
86use core::ptr::{self, NonNull};
87use core::task::{Context, Poll, Waker};
88use futures::channel::oneshot;
89use futures::stream::{FuturesUnordered, StreamExt};
90use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
91use table::{TableDebug, TableId};
92use wasmtime_environ::component::{
93 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
94 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
95 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
96 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
97 TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
98};
99use wasmtime_environ::packed_option::ReservedValue;
100use wasmtime_environ::{ModuleInternedTypeIndex, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
101#[cfg(feature = "gc")]
102use wasmtime_unwinder::Unwind;
103
104pub use abort::JoinHandle;
105pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
106pub use future_stream_any::{FutureAny, StreamAny};
107pub use futures_and_streams::{
108 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
109 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
110 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
111};
112pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
113#[cfg(feature = "task-group-hook")]
114pub use task_group_hook::TaskGroupHook;
115pub use task_group_hook::TaskGroupId;
116
117mod abort;
118mod error_contexts;
119mod func;
120mod future_stream_any;
121mod futures_and_streams;
122pub(crate) mod table;
123#[cfg(feature = "task-group-hook")]
124mod task_group_hook;
125#[cfg(not(feature = "task-group-hook"))]
126mod task_group_hook_disabled;
127#[cfg(not(feature = "task-group-hook"))]
128use task_group_hook_disabled as task_group_hook;
129pub(crate) mod tls;
130
131const BLOCKED: u32 = 0xffff_ffff;
134
135#[derive(Clone, Copy, Eq, PartialEq, Debug)]
137pub enum Status {
138 Starting = 0,
139 Started = 1,
140 Returned = 2,
141 StartCancelled = 3,
142 ReturnCancelled = 4,
143}
144
145impl Status {
146 pub fn pack(self, waitable: Option<u32>) -> u32 {
152 assert!(matches!(self, Status::Returned) == waitable.is_none());
153 let waitable = waitable.unwrap_or(0);
154 assert!(waitable < (1 << 28));
155 (waitable << 4) | (self as u32)
156 }
157}
158
159#[derive(Clone, Copy, Debug)]
162enum Event {
163 None,
164 Subtask {
165 status: Status,
166 },
167 StreamRead {
168 code: ReturnCode,
169 pending: Option<(TypeStreamTableIndex, u32)>,
170 },
171 StreamWrite {
172 code: ReturnCode,
173 pending: Option<(TypeStreamTableIndex, u32)>,
174 },
175 FutureRead {
176 code: ReturnCode,
177 pending: Option<(TypeFutureTableIndex, u32)>,
178 },
179 FutureWrite {
180 code: ReturnCode,
181 pending: Option<(TypeFutureTableIndex, u32)>,
182 },
183 Cancelled,
184}
185
186impl Event {
187 fn parts(self) -> (u32, u32) {
192 const EVENT_NONE: u32 = 0;
193 const EVENT_SUBTASK: u32 = 1;
194 const EVENT_STREAM_READ: u32 = 2;
195 const EVENT_STREAM_WRITE: u32 = 3;
196 const EVENT_FUTURE_READ: u32 = 4;
197 const EVENT_FUTURE_WRITE: u32 = 5;
198 const EVENT_CANCELLED: u32 = 6;
199 match self {
200 Event::None => (EVENT_NONE, 0),
201 Event::Cancelled => (EVENT_CANCELLED, 0),
202 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
203 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
204 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
205 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
206 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
207 }
208 }
209}
210
211mod callback_code {
213 pub const EXIT: u32 = 0;
214 pub const YIELD: u32 = 1;
215 pub const WAIT: u32 = 2;
216}
217
218const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
220const START_FLAG_ASYNC_CALLER: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLER as u32;
221
222pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
228 store: StoreContextMut<'a, T>,
229 get_data: fn(&mut T) -> D::Data<'_>,
230}
231
232impl<'a, T, D> Access<'a, T, D>
233where
234 D: HasData + ?Sized,
235 T: 'static,
236{
237 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
239 Self { store, get_data }
240 }
241
242 pub fn data_mut(&mut self) -> &mut T {
244 self.store.data_mut()
245 }
246
247 pub fn get(&mut self) -> D::Data<'_> {
249 (self.get_data)(self.data_mut())
250 }
251
252 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
256 where
257 T: 'static,
258 {
259 let accessor = Accessor {
260 get_data: self.get_data,
261 token: StoreToken::new(self.store.as_context_mut()),
262 };
263 self.store
264 .as_context_mut()
265 .spawn_with_accessor(accessor, task)
266 }
267
268 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
271 self.get_data
272 }
273}
274
275impl<'a, T, D> AsContext for Access<'a, T, D>
276where
277 D: HasData + ?Sized,
278 T: 'static,
279{
280 type Data = T;
281
282 fn as_context(&self) -> StoreContext<'_, T> {
283 self.store.as_context()
284 }
285}
286
287impl<'a, T, D> AsContextMut for Access<'a, T, D>
288where
289 D: HasData + ?Sized,
290 T: 'static,
291{
292 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
293 self.store.as_context_mut()
294 }
295}
296
297pub struct Accessor<T: 'static, D = HasSelf<T>>
357where
358 D: HasData + ?Sized,
359{
360 token: StoreToken<T>,
361 get_data: fn(&mut T) -> D::Data<'_>,
362}
363
364pub trait AsAccessor {
381 type Data: 'static;
383
384 type AccessorData: HasData + ?Sized;
387
388 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
390}
391
392impl<T: AsAccessor + ?Sized> AsAccessor for &T {
393 type Data = T::Data;
394 type AccessorData = T::AccessorData;
395
396 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
397 T::as_accessor(self)
398 }
399}
400
401impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
402 type Data = T;
403 type AccessorData = D;
404
405 fn as_accessor(&self) -> &Accessor<T, D> {
406 self
407 }
408}
409
410const _: () = {
433 const fn assert<T: Send + Sync>() {}
434 assert::<Accessor<UnsafeCell<u32>>>();
435};
436
437impl<T> Accessor<T> {
438 pub(crate) fn new(token: StoreToken<T>) -> Self {
447 Self {
448 token,
449 get_data: |x| x,
450 }
451 }
452}
453
454impl<T, D> Accessor<T, D>
455where
456 D: HasData + ?Sized,
457{
458 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
476 tls::get(|vmstore| {
477 fun(Access {
478 store: self.token.as_context_mut(vmstore),
479 get_data: self.get_data,
480 })
481 })
482 }
483
484 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
487 self.get_data
488 }
489
490 pub fn with_getter<D2: HasData>(
507 &self,
508 get_data: fn(&mut T) -> D2::Data<'_>,
509 ) -> Accessor<T, D2> {
510 Accessor {
511 token: self.token,
512 get_data,
513 }
514 }
515
516 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
532 where
533 T: 'static,
534 {
535 let accessor = self.clone_for_spawn();
536 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
537 }
538
539 fn clone_for_spawn(&self) -> Self {
540 Self {
541 token: self.token,
542 get_data: self.get_data,
543 }
544 }
545
546 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
582 self.with(|mut access| {
583 let store = access.as_context_mut().0;
584 let state = store.concurrent_state_mut_without_forcing_current_thread();
585 if state.interesting_tasks == 0 {
586 Poll::Ready(())
587 } else {
588 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
589 Poll::Pending
590 }
591 })
592 }
593
594 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
611 self.with(|mut access| {
612 let store = access.as_context_mut().0;
613 let (_, _, _, raw_options) = func.abi_info(store);
614 let instance = func.instance().runtime_instance(raw_options.instance);
615 let state = store.instance_state(instance).concurrent_state();
616 if state.backpressure == 0 {
617 Poll::Ready(())
618 } else {
619 store
620 .concurrent_state_mut_without_forcing_current_thread()
621 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
622 Poll::Pending
623 }
624 })
625 }
626}
627
628pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
650 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
651where
652 D: HasData + ?Sized,
653{
654 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
656}
657
658impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
659where
660 T: 'static,
661 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
662 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
663 Fut: Future<Output = Result<()>> + Send + 'fut,
664 D: HasData,
665{
666 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
667 (self)(accessor)
668 }
669}
670
671enum CallerInfo {
674 Async {
676 params: Vec<ValRaw>,
677 has_result: bool,
678 },
679 Sync {
681 params: Vec<ValRaw>,
682 result_count: u32,
683 },
684}
685
686enum WaitMode {
688 Fiber(StoreFiber<'static>),
690 Callback(Instance),
693}
694
695impl fmt::Debug for WaitMode {
696 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697 match self {
698 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
699 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
700 }
701 }
702}
703
704#[derive(Debug)]
706enum SuspendReason {
707 Waiting {
710 set: TableId<WaitableSet>,
711 thread: QualifiedThreadId,
712 },
713 YieldingToSubtask { thread: QualifiedThreadId },
716 NeedWork,
719 Yielding { thread: QualifiedThreadId },
722 ExplicitlySuspending { thread: QualifiedThreadId },
725}
726
727enum GuestCallKind {
729 DeliverEvent {
732 instance: Instance,
734 set: Option<TableId<WaitableSet>>,
739 },
740 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
743 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
744}
745
746impl fmt::Debug for GuestCallKind {
747 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
748 match self {
749 Self::DeliverEvent { instance, set } => f
750 .debug_struct("DeliverEvent")
751 .field("instance", instance)
752 .field("set", set)
753 .finish(),
754 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
755 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
756 }
757 }
758}
759
760#[derive(Copy, Clone, Debug)]
762pub enum SuspensionTarget {
763 Resume(u32),
764 Promote(u32),
765 None,
766}
767
768#[derive(Copy, Clone, Debug)]
770pub enum ResumeThread {
771 Promote,
772 Resume,
773 ResumeLater,
774}
775
776#[derive(Debug)]
778struct GuestCall {
779 thread: QualifiedThreadId,
780 kind: GuestCallKind,
781}
782
783impl GuestCall {
784 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
794 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
795 let async_typed = task.async_typed;
796 let instance = task.instance;
797 let state = store.instance_state(instance).concurrent_state();
798
799 let ready = match &self.kind {
800 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
801 GuestCallKind::StartImplicit(_) => {
802 !async_typed || !(state.do_not_enter || state.backpressure > 0)
803 }
804 GuestCallKind::StartExplicit(_) => true,
805 };
806 log::trace!(
807 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
808 state.do_not_enter,
809 state.backpressure
810 );
811 Ok(ready)
812 }
813}
814
815enum WorkerItem {
817 GuestCall(GuestCall),
818 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
819}
820
821enum WorkItem {
824 PushFuture(AlwaysMut<HostTaskFuture>),
826 ResumeFiber {
828 instance: RuntimeInstance,
829 thread: QualifiedThreadId,
830 fiber: StoreFiber<'static>,
831 },
832 ResumeThread {
834 instance: RuntimeInstance,
835 thread: QualifiedThreadId,
836 },
837 GuestCall {
839 instance: RuntimeInstance,
840 call: GuestCall,
841 },
842 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
844}
845
846struct ResumingFiber<'a> {
852 store: &'a mut StoreOpaque,
853 fiber: Option<StoreFiber<'static>>,
854}
855
856impl Drop for ResumingFiber<'_> {
857 fn drop(&mut self) {
858 if let Some(mut fiber) = self.fiber.take() {
859 fiber.dispose(self.store);
860 }
861 }
862}
863
864impl fmt::Debug for WorkItem {
865 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
866 match self {
867 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
868 Self::ResumeFiber {
869 instance, thread, ..
870 } => f
871 .debug_struct("ResumeFiber")
872 .field("instance", instance)
873 .field("thread", thread)
874 .finish(),
875 Self::ResumeThread { instance, thread } => f
876 .debug_struct("ResumeThread")
877 .field("instance", instance)
878 .field("thread", thread)
879 .finish(),
880 Self::GuestCall { instance, call } => f
881 .debug_struct("GuestCall")
882 .field("instance", instance)
883 .field("call", call)
884 .finish(),
885 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
886 }
887 }
888}
889
890#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
892pub(crate) enum WaitResult {
893 Cancelled,
894 Completed,
895}
896
897pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
905 store: &mut dyn VMStore,
906 host_task: EnteredHostTask,
907 future: impl Future<Output = Result<R>> + Send + 'static,
908) -> Result<R> {
909 let mut future = Box::pin(future);
916 let poll = tls::set(store, || {
917 future
918 .as_mut()
919 .poll(&mut Context::from_waker(&Waker::noop()))
920 });
921
922 let caller = match host_task {
923 Some(caller) => caller,
924 None => bail_bug!("host task wasn't created but should have been"),
925 };
926
927 let task = match poll {
928 Poll::Ready(result) => return result,
930
931 Poll::Pending => {
936 let Some(task) = store.materialize_host_task_id()? else {
937 bail_bug!("current thread is not a host thread")
938 };
939
940 let future = Box::pin(async move {
943 let result = run_with_host_task_set(task, future).await??;
944 tls::get(move |store| {
945 let state = store.concurrent_state_mut()?;
946 let host_state = &mut state.get_mut(task)?.state;
947 assert!(matches!(host_state, HostTaskState::CalleeStarted));
948 *host_state = HostTaskState::CalleeFinished(Box::new(result));
949
950 Waitable::Host(task).set_event(
951 state,
952 Some(Event::Subtask {
953 status: Status::Returned,
954 }),
955 )?;
956
957 Ok(())
958 })
959 }) as HostTaskFuture;
960
961 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
962 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
963
964 let state = store.concurrent_state_mut()?;
965 state.push_future(future);
966
967 let set = state.get_mut(caller.thread)?.sync_call_set;
968 Waitable::Host(task).join(state, Some(set))?;
969
970 store.suspend(SuspendReason::Waiting {
971 set,
972 thread: caller,
973 })?;
974
975 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
979 task
980 }
981 };
982
983 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
985 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
986 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
987 Ok(result) => *result,
988 Err(_) => bail_bug!("host task finished with wrong type of result"),
989 }),
990 _ => bail_bug!("unexpected host task state after completion"),
991 }
992}
993
994fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
996 match call.kind {
997 GuestCallKind::DeliverEvent { instance, set } => {
998 if let Some(set) = set {
1001 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1002 }
1003 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1004 Some(pair) => pair,
1005 None => match set {
1006 Some(set) => {
1011 log::trace!(
1012 "event for {:?} on {set:?} no longer present; waiting again",
1013 call.thread
1014 );
1015 return instance.wait_with_callback(
1016 store.store_opaque_mut(),
1017 call.thread,
1018 set,
1019 );
1020 }
1021 None => (Event::None, None),
1026 },
1027 };
1028 let state = store.concurrent_state_mut()?;
1029 if let Some(set) = set
1034 && waitable.is_none()
1035 && !state.get_mut(set)?.ready.is_empty()
1036 {
1037 state.wake_waiter(set)?;
1038 }
1039 let task = state.get_mut(call.thread.task)?;
1040 let runtime_instance = task.instance;
1041 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1042
1043 log::trace!(
1044 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1045 call.thread,
1046 );
1047
1048 instance.handle_callback_code(store, call.thread, runtime_instance, |store| {
1049 invoke_callback(store, call.thread.task, event, handle)
1050 })?;
1051 }
1052 GuestCallKind::StartImplicit(fun) => {
1053 fun(store)?;
1054 }
1055 GuestCallKind::StartExplicit(fun) => {
1056 fun(store)?;
1057 }
1058 }
1059
1060 Ok(())
1061}
1062
1063fn invoke_callback(
1066 store: &mut dyn VMStore,
1067 task: TableId<GuestTask>,
1068 event: Event,
1069 handle: u32,
1070) -> Result<u32> {
1071 let Some(callback) = store.concurrent_state_mut()?.get_mut(task)?.callback.take() else {
1072 bail_bug!("guest task callback field not present")
1073 };
1074
1075 let code = callback(store, event, handle)?;
1076
1077 store.concurrent_state_mut()?.get_mut(task)?.callback = Some(callback);
1078
1079 Ok(code)
1080}
1081
1082impl<T> Store<T> {
1083 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1085 where
1086 T: Send + 'static,
1087 {
1088 ensure!(
1089 self.as_context().0.concurrency_support(),
1090 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1091 );
1092 self.as_context_mut().run_concurrent(fun).await
1093 }
1094
1095 #[doc(hidden)]
1096 pub fn assert_concurrent_state_empty(&mut self) {
1097 self.as_context_mut().assert_concurrent_state_empty();
1098 }
1099
1100 #[doc(hidden)]
1101 pub fn concurrent_state_table_size(&mut self) -> usize {
1102 self.as_context_mut().concurrent_state_table_size()
1103 }
1104
1105 pub fn spawn(
1107 &mut self,
1108 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1109 ) -> Result<JoinHandle>
1110 where
1111 T: 'static,
1112 {
1113 self.as_context_mut().spawn(task)
1114 }
1115}
1116
1117impl<T> StoreContextMut<'_, T> {
1118 #[doc(hidden)]
1129 pub fn assert_concurrent_state_empty(self) {
1130 let store = self.0;
1131 store
1132 .store_data_mut()
1133 .components
1134 .assert_instance_states_empty();
1135 let state = store.concurrent_state_mut().unwrap();
1136 assert!(
1137 state.table.get_mut().is_empty(),
1138 "non-empty table: {:?}",
1139 state.table.get_mut()
1140 );
1141 assert!(state.switch_item.is_none());
1142 assert!(state.next_switch_item.is_none());
1143 assert!(state.high_priority.is_empty());
1144 assert!(state.low_priority.is_empty());
1145 assert!(state.saved_next_switch_items.is_empty());
1146 assert!(state.unforced_current_thread.is_none());
1147 assert!(state.deferred_host_call_context.is_none());
1148 assert!(state.futures_mut().unwrap().is_empty());
1149 assert!(state.global_error_context_ref_counts.is_empty());
1150 }
1151
1152 #[doc(hidden)]
1157 pub fn concurrent_state_table_size(&mut self) -> usize {
1158 self.0.concurrent_state_mut().unwrap().table.get_mut().len()
1159 }
1160
1161 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1171 where
1172 T: 'static,
1173 {
1174 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1175 self.spawn_with_accessor(accessor, task)
1176 }
1177
1178 fn spawn_with_accessor<D>(
1181 self,
1182 accessor: Accessor<T, D>,
1183 task: impl for<'fut> AccessorTask<'fut, T, D>,
1184 ) -> Result<JoinHandle>
1185 where
1186 T: 'static,
1187 D: HasData + ?Sized,
1188 {
1189 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1193 self.0
1194 .concurrent_state_mut()?
1195 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1196 Ok(handle)
1197 }
1198
1199 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1283 where
1284 T: Send + 'static,
1285 {
1286 ensure!(
1287 self.0.concurrency_support(),
1288 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1289 );
1290 self.do_run_concurrent(fun, false).await
1291 }
1292
1293 pub(super) async fn run_concurrent_trap_on_idle<R>(
1294 self,
1295 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1296 ) -> Result<R> {
1297 self.do_run_concurrent(fun, true).await
1298 }
1299
1300 async fn do_run_concurrent<R>(
1301 mut self,
1302 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1303 trap_on_idle: bool,
1304 ) -> Result<R> {
1305 debug_assert!(self.0.concurrency_support());
1306 let already_running = self
1307 .0
1308 .concurrent_state_mut_already_forced_current_thread()
1309 .event_loop_running;
1310 if already_running {
1311 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1312 }
1313 let token = StoreToken::new(self.as_context_mut());
1314
1315 struct Dropper<'a, T: 'static, V> {
1316 store: StoreContextMut<'a, T>,
1317 value: ManuallyDrop<V>,
1318 }
1319
1320 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1321 fn drop(&mut self) {
1322 self.store
1323 .0
1324 .concurrent_state_mut_already_forced_current_thread()
1325 .event_loop_running = false;
1326
1327 tls::set(self.store.0, || {
1328 unsafe { ManuallyDrop::drop(&mut self.value) }
1333 });
1334 }
1335 }
1336
1337 let accessor = &Accessor::new(token);
1338 self.0
1339 .concurrent_state_mut_already_forced_current_thread()
1340 .event_loop_running = true;
1341 let dropper = &mut Dropper {
1342 store: self,
1343 value: ManuallyDrop::new(fun(accessor)),
1344 };
1345 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1347
1348 let result = dropper
1349 .store
1350 .as_context_mut()
1351 .poll_until(future, trap_on_idle)
1352 .await;
1353
1354 if result.is_err() {
1355 dropper.store.0.set_trapped();
1356 }
1357
1358 result
1359 }
1360
1361 async fn poll_until<R>(
1367 mut self,
1368 mut future: Pin<&mut impl Future<Output = R>>,
1369 trap_on_idle: bool,
1370 ) -> Result<R> {
1371 struct Reset<'a, T: 'static> {
1372 store: StoreContextMut<'a, T>,
1373 futures: Option<FuturesUnordered<HostTaskFuture>>,
1374 }
1375
1376 impl<'a, T> Drop for Reset<'a, T> {
1377 fn drop(&mut self) {
1378 if let Some(futures) = self.futures.take() {
1379 *self
1380 .store
1381 .0
1382 .concurrent_state_mut_already_forced_current_thread()
1383 .futures
1384 .get_mut() = Some(futures);
1385 }
1386 }
1387 }
1388
1389 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1393 let mut turns_without_yield = 0;
1394
1395 loop {
1396 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1400 let mut reset = Reset {
1401 store: self.as_context_mut(),
1402 futures,
1403 };
1404 let mut next = match reset.futures.as_mut() {
1405 Some(f) => pin!(f.next()),
1406 None => bail_bug!("concurrent state missing futures field"),
1407 };
1408
1409 enum PollResult<R> {
1410 Complete(R),
1411 ProcessWork {
1412 ready: Option<WorkItem>,
1413 low_priority: bool,
1414 },
1415 }
1416
1417 let result = future::poll_fn(|cx| {
1418 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1421 return Poll::Ready(Ok(PollResult::Complete(value)));
1422 }
1423
1424 if reset.store.0.trapped() {
1433 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1434 }
1435
1436 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1440 Poll::Ready(Some(output)) => {
1441 match output {
1442 Err(e) => return Poll::Ready(Err(e)),
1443 Ok(()) => {}
1444 }
1445 Poll::Ready(true)
1446 }
1447 Poll::Ready(None) => Poll::Ready(false),
1448 Poll::Pending => Poll::Pending,
1449 };
1450
1451 let state = reset.store.0.concurrent_state_mut()?;
1466 let mut ready = state.switch_item.take();
1467 let mut low_priority = false;
1468 if ready.is_none() {
1469 ready = state.high_priority.pop_back();
1470 if ready.is_none() {
1471 ready = state.low_priority.pop_back();
1472 low_priority = true;
1473 }
1474 }
1475 if ready.is_some() {
1476 return Poll::Ready(Ok(PollResult::ProcessWork {
1477 ready,
1478 low_priority,
1479 }));
1480 }
1481
1482 return match next {
1486 Poll::Ready(true) => {
1487 Poll::Ready(Ok(PollResult::ProcessWork {
1493 ready: None,
1494 low_priority: false,
1495 }))
1496 }
1497 Poll::Ready(false) => {
1498 if let Poll::Ready(value) =
1502 tls::set(reset.store.0, || future.as_mut().poll(cx))
1503 {
1504 Poll::Ready(Ok(PollResult::Complete(value)))
1505 } else {
1506 if trap_on_idle {
1512 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1519 Trap::CannotBlockSyncTask.into()
1520 } else {
1521 Trap::AsyncDeadlock.into()
1523 }))
1524 } else {
1525 Poll::Pending
1529 }
1530 }
1531 }
1532 Poll::Pending => Poll::Pending,
1537 };
1538 })
1539 .await;
1540
1541 drop(reset);
1545
1546 match result? {
1547 PollResult::Complete(value) => break Ok(value),
1550 PollResult::ProcessWork {
1553 mut ready,
1554 low_priority,
1555 } => {
1556 if low_priority {
1584 if let Some(item) = ready.take() {
1585 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1586 }
1587 self.0.yield_now().await;
1588 turns_without_yield = 0;
1589 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1590 }
1591
1592 if let Some(item) = ready {
1593 self.as_context_mut().handle_work_item(item).await?;
1594 }
1595
1596 turns_without_yield += 1;
1597 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1598 turns_without_yield = 0;
1599 self.0.yield_now().await;
1600 }
1601 }
1602 }
1603 }
1604 }
1605
1606 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1608 let item = match item {
1612 WorkItem::ResumeFiber {
1613 instance,
1614 thread,
1615 fiber,
1616 } => {
1617 let future = self.0.resume_fiber(fiber);
1618 log::trace!(
1619 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1620 );
1621 return futures::future::Either::Left(future);
1622 }
1623 item => item,
1624 };
1625 futures::future::Either::Right(async move {
1626 log::trace!("handle work item {item:?}");
1627 match item {
1628 WorkItem::PushFuture(future) => {
1629 self.0
1630 .concurrent_state_mut()?
1631 .futures_mut()?
1632 .push(future.into_inner());
1633 }
1634 WorkItem::ResumeFiber { .. } => unreachable!(),
1635 WorkItem::ResumeThread { thread, .. } => {
1636 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1637 if !matches!(slot, GuestThreadState::Ready { .. }) {
1638 bail_bug!("cannot resume non-pending thread {thread:?}");
1639 }
1640 let GuestThreadState::Ready { fiber } =
1641 mem::replace(slot, GuestThreadState::Running)
1642 else {
1643 unreachable!()
1644 };
1645 self.0.resume_fiber(fiber).await?;
1646 }
1647 WorkItem::GuestCall { call, .. } => {
1648 if call.is_ready(self.0)? {
1649 self.0
1650 .concurrent_state_mut()?
1651 .get_mut(call.thread.thread)?
1652 .wake_on_cancel = WakeOnCancel::None;
1653 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1654 } else {
1655 let state = self.0.concurrent_state_mut()?;
1656 let task = state.get_mut(call.thread.task)?;
1657 if !task.starting_sent {
1658 task.starting_sent = true;
1659 if let GuestCallKind::StartImplicit(_) = &call.kind {
1660 Waitable::Guest(call.thread.task).set_event(
1661 state,
1662 Some(Event::Subtask {
1663 status: Status::Starting,
1664 }),
1665 )?;
1666 }
1667 }
1668
1669 let instance = state.get_mut(call.thread.task)?.instance;
1670 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1679 let state = self.0.concurrent_state_mut()?;
1680 if !state.get_mut(*set)?.ready.is_empty() {
1681 state.wake_waiter(*set)?;
1682 }
1683 }
1684 self.0
1685 .instance_state(instance)
1686 .concurrent_state()
1687 .pending
1688 .insert(call.thread, call.kind);
1689
1690 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1694 }
1695 }
1696 WorkItem::WorkerFunction(fun) => {
1697 self.run_on_worker(WorkerItem::Function(fun)).await?;
1698 }
1699 }
1700
1701 Ok(())
1702 })
1703 }
1704
1705 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1707 let state = self.0.concurrent_state_mut()?;
1708 if state.worker_item.is_some() {
1709 bail_bug!("worker item already set when taking worker fiber");
1710 }
1711 let worker = if let Some(fiber) = state.worker.take() {
1712 fiber
1713 } else {
1714 unsafe {
1733 fiber::make_fiber_unchecked(self.0, move |store| {
1734 loop {
1735 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1736 bail_bug!("worker_item not present when resuming fiber")
1737 };
1738 match item {
1739 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1740 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1741 }
1742
1743 store.suspend(SuspendReason::NeedWork)?;
1744 }
1745 })?
1746 }
1747 };
1748
1749 self.0
1752 .concurrent_state_mut_already_forced_current_thread()
1753 .worker_item = Some(item);
1754
1755 self.0.resume_fiber(worker).await
1756 }
1757
1758 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1763 where
1764 T: 'static,
1765 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1766 + Send
1767 + Sync
1768 + 'static,
1769 R: Send + Sync + 'static,
1770 {
1771 let token = StoreToken::new(self);
1772 async move {
1773 let mut accessor = Accessor::new(token);
1774 closure(&mut accessor).await
1775 }
1776 }
1777
1778 pub(crate) async fn start_instance(
1779 &mut self,
1780 instance: ModuleInstance,
1781 callee: Option<RuntimeInstance>,
1782 ) -> Result<ModuleInstance> {
1783 let (tx, rx) = oneshot::channel();
1784 let token = StoreToken::new(self.as_context_mut());
1785 self.0.queue_task(move |store| {
1786 _ = tx.send(
1787 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1788 .map(|()| instance),
1789 );
1790 Ok(())
1791 })?;
1792 self.as_context_mut()
1793 .run_concurrent_trap_on_idle(async |_| {
1794 rx.await
1795 .map_err(|_| format_err!("oneshot channel canceled"))
1796 })
1797 .await??
1798 }
1799}
1800
1801pub type EnteredHostTask = Option<QualifiedThreadId>;
1808
1809impl StoreOpaque {
1810 #[inline]
1814 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1815 if !self.concurrency_support() {
1817 return Ok(CurrentThread::None);
1818 }
1819
1820 if !self
1823 .vm_store_context_mut()
1824 .current_thread_mut()
1825 .is_deferred()
1826 {
1827 return Ok(self
1828 .concurrent_state_mut_already_forced_current_thread()
1829 .unforced_current_thread);
1830 }
1831
1832 self.force_deferred_current_thread()
1833 }
1834
1835 #[cold]
1838 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1839 let state = self.concurrent_state_mut_without_forcing_current_thread();
1848 let id = match state.unforced_current_thread.guest_task() {
1849 Some(task) => state.get_mut(task)?.instance.instance,
1850 None => bail_bug!("deferred component-model thread with non-guest base"),
1851 };
1852
1853 let mut frames = Vec::new();
1856 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1857 while let Some(ptr) = cur.as_deferred() {
1858 let deferred = unsafe { ptr.as_non_null().as_ref() };
1863 frames.push((
1864 deferred.callee_async != 0,
1865 deferred.callee_instance,
1866 deferred.saved_context,
1867 ));
1868 cur = deferred.parent;
1869 }
1870
1871 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1875
1876 let current_context = *self.vm_store_context_mut().component_context_mut();
1879
1880 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1884 *self.vm_store_context_mut().component_context_mut() = saved_context;
1888 let callee = RuntimeInstance {
1889 instance: id,
1890 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1891 };
1892 self.enter_guest_sync_call(callee_async, callee)?;
1893 }
1894
1895 *self.vm_store_context_mut().component_context_mut() = current_context;
1897
1898 Ok(self
1899 .concurrent_state_mut_without_forcing_current_thread()
1900 .unforced_current_thread)
1901 }
1902
1903 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1904 match self.current_thread()?.guest() {
1905 Some(id) => Ok(*id),
1906 None => bail_bug!("current thread is not a guest thread"),
1907 }
1908 }
1909
1910 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1914 match self.current_thread()? {
1915 CurrentThread::Host(id) => Ok(Some(id)),
1916 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1917 _ => bail_bug!("current thread is not a host thread"),
1918 }
1919 }
1920
1921 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1924 Ok(self
1925 .concurrent_state_mut()?
1926 .materialize_current_host_task_id()?)
1927 }
1928
1929 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1930 log::trace!("enter sync-typed call {callee:?}");
1931 let state = self.instance_state(callee).concurrent_state();
1932 let old_do_not_suspend = state.do_not_suspend;
1933 state.do_not_suspend = true;
1934
1935 let thread = self.current_guest_thread()?;
1936 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1937 if thread.old_do_not_suspend.is_some() {
1938 bail_bug!("current thread already has `old_do_not_suspend` value");
1939 }
1940
1941 thread.old_do_not_suspend = Some(old_do_not_suspend);
1942
1943 Ok(())
1944 }
1945
1946 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1947 log::trace!("exit sync-typed call {callee:?}");
1948 let thread = self.current_guest_thread()?;
1949 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1950 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1951 bail_bug!("current thread missing `old_do_not_suspend` value");
1952 };
1953 let state = self.instance_state(callee).concurrent_state();
1954 state.do_not_suspend = old_do_not_suspend;
1955 Ok(())
1956 }
1957
1958 pub(crate) fn enter_guest_sync_call(
1970 &mut self,
1971 callee_async_typed: bool,
1972 callee: RuntimeInstance,
1973 ) -> Result<()> {
1974 log::trace!("enter sync-lifted call {callee:?}");
1975 if !self.concurrency_support() {
1976 return self.enter_call_not_concurrent();
1977 }
1978
1979 let state = self.concurrent_state_mut()?;
1983 let item = state.next_switch_item.take();
1984 state.saved_next_switch_items.push(item);
1985
1986 let thread = self.current_thread()?;
1987 let caller = if let Some(thread) = thread.guest() {
1988 Caller::Guest { thread: *thread }
1989 } else {
1990 Caller::Host {
1991 tx: None,
1992 caller: self.materialize_host_task_id()?,
1993 }
1994 };
1995
1996 let state = self.concurrent_state_mut()?;
1997 let guest_thread = GuestTask::new(
1998 state,
1999 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2000 LiftResult {
2001 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2002 ty: TypeTupleIndex::reserved_value(),
2003 memory: None,
2004 string_encoding: StringEncoding::Utf8,
2005 },
2006 caller,
2007 None,
2008 callee,
2009 callee_async_typed,
2010 false,
2011 )?;
2012
2013 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2014 guest_thread.thread,
2015 self,
2016 callee.index,
2017 )?;
2018 self.set_thread(guest_thread)?;
2019
2020 if !callee_async_typed {
2021 self.enter_sync_call(callee)?;
2022 }
2023
2024 Ok(())
2025 }
2026
2027 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2035 if !self.concurrency_support() {
2036 return Ok(self.exit_call_not_concurrent());
2037 }
2038
2039 let thread = match self.current_thread()?.guest() {
2040 Some(t) => *t,
2041 None => bail_bug!("expected task when exiting"),
2042 };
2043 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2044 let instance = task.instance;
2045
2046 let caller = match &task.caller {
2047 &Caller::Guest { thread } => thread.into(),
2048 &Caller::Host { caller, .. } => caller
2049 .map(CurrentThread::Host)
2050 .unwrap_or(CurrentThread::None),
2051 };
2052 task.lift_result = None;
2053 task.exited = true;
2054 let async_typed = task.async_typed;
2055
2056 if !async_typed {
2057 self.exit_sync_call(instance)?;
2058 }
2059
2060 self.set_thread(caller)?;
2061
2062 log::trace!("exit sync-lifted call {instance:?}");
2063
2064 if async_typed {
2065 self.switch_or_trap_if_may_not_suspend(instance)?;
2070 }
2071
2072 self.cleanup_thread(thread, instance)?;
2073
2074 self.concurrent_state_mut()?
2075 .decrement_task_ref_count(thread.task)?;
2076
2077 let state = self.concurrent_state_mut()?;
2078 let Some(item) = state.saved_next_switch_items.pop() else {
2079 bail_bug!("unable to pop from `saved_next_switch_items`");
2080 };
2081 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2082 state.push_high_priority(item);
2085 bail_bug!("`next_switch_item` unexpectedly already set");
2086 }
2087
2088 Ok(())
2089 }
2090
2091 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2098 if !self.concurrency_support() {
2099 self.enter_call_not_concurrent()?;
2100 return Ok(None);
2101 }
2102 let caller = self.current_guest_thread()?;
2103 log::trace!("new deferred host task with caller {caller:?}");
2104
2105 self.set_thread(CurrentThread::DeferredHost(caller))?;
2106 let state = self.concurrent_state_mut()?;
2107 debug_assert!(state.deferred_host_call_context.is_none());
2108 state.deferred_host_call_context = Some(CallContext::default());
2109 state.debug_assert_deferred_host_invariant();
2110 Ok(Some(caller))
2111 }
2112
2113 pub(crate) fn host_task_delete(
2120 &mut self,
2121 original_task: EnteredHostTask,
2122 materialized_task: Option<TableId<HostTask>>,
2123 ) -> Result<()> {
2124 match original_task {
2125 Some(caller) => {
2126 self.set_thread(caller)?;
2127 if materialized_task.is_none() {
2128 let state = self.concurrent_state_mut()?;
2129 let context = state
2130 .deferred_host_call_context
2131 .take()
2132 .expect("deferred host call context should be present");
2133 debug_assert!(context.is_empty());
2134 state.debug_assert_deferred_host_invariant();
2135 }
2136 log::trace!(
2137 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2138 );
2139 if let Some(task) = materialized_task {
2140 self.concurrent_state_mut()?.delete_host_task(task)?;
2141 }
2142 }
2143 None => {
2144 debug_assert!(materialized_task.is_none());
2145 self.exit_call_not_concurrent();
2146 }
2147 }
2148 Ok(())
2149 }
2150
2151 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2154 self.component_instance_mut(instance.instance)
2155 .instance_state(instance.index)
2156 }
2157
2158 fn instance_state_and_handle_counter(
2161 &mut self,
2162 instance: RuntimeInstance,
2163 ) -> (&mut InstanceState, &mut HandleCounter) {
2164 let (component, counter) = self
2165 .store_data_mut()
2166 .component_instance_and_handle_counter_mut(instance.instance);
2167 (component.instance_state(instance.index), counter)
2168 }
2169
2170 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2176 let thread = thread.into();
2177 let state = self.concurrent_state_mut()?;
2178 state.debug_assert_deferred_host_invariant();
2179 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2180
2181 state.handle_thread_switch(old_thread, thread)?;
2182
2183 if let Some(old_thread) = old_thread.guest() {
2191 let old_context = *self.vm_store_context_mut().component_context_mut();
2192 self.concurrent_state_mut()?
2193 .get_mut(old_thread.thread)?
2194 .context = old_context;
2195 }
2196 if cfg!(debug_assertions) {
2197 *self.vm_store_context_mut().component_context_mut() =
2198 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2199 }
2200 if let Some(thread) = thread.guest() {
2201 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2202 let context = thread.context;
2203 if cfg!(debug_assertions) {
2204 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2205 }
2206 *self.vm_store_context_mut().component_context_mut() = context;
2207 }
2208
2209 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2211 VMLazyThread::none()
2212 } else {
2213 VMLazyThread::forced()
2214 };
2215
2216 Ok(old_thread)
2217 }
2218
2219 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2221 if self.switch_if_may_not_suspend(instance)? {
2222 Ok(())
2223 } else {
2224 Err(Trap::CannotBlockSyncTask.into())
2225 }
2226 }
2227
2228 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2232 self.concurrent_state_mut()?;
2236
2237 Ok(!self.concurrency_support()
2238 || !self
2239 .instance_state(instance)
2240 .concurrent_state()
2241 .do_not_suspend
2242 || self
2243 .concurrent_state_mut()?
2244 .promote_instance_local_thread_work_item(instance)?)
2245 }
2246
2247 fn enter_instance(&mut self, instance: RuntimeInstance) {
2251 log::trace!("enter {instance:?}");
2252 self.instance_state(instance)
2253 .concurrent_state()
2254 .do_not_enter = true;
2255 }
2256
2257 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2261 log::trace!("exit {instance:?}");
2262 self.instance_state(instance)
2263 .concurrent_state()
2264 .do_not_enter = false;
2265 self.partition_pending(instance)
2266 }
2267
2268 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2276 for (thread, kind) in
2277 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2278 {
2279 let call = GuestCall { thread, kind };
2280 if call.is_ready(self)? {
2281 self.concurrent_state_mut()?
2282 .push_high_priority(WorkItem::GuestCall { instance, call });
2283 } else {
2284 self.instance_state(instance)
2285 .concurrent_state()
2286 .pending
2287 .insert(call.thread, call.kind);
2288 }
2289 }
2290
2291 if let Some(waker) = self
2292 .concurrent_state_mut()?
2293 .ready_for_concurrent_call_waker
2294 .take()
2295 {
2296 waker.wake();
2297 }
2298
2299 Ok(())
2300 }
2301
2302 pub(crate) fn backpressure_modify(
2304 &mut self,
2305 caller_instance: RuntimeInstance,
2306 modify: impl FnOnce(u16) -> Option<u16>,
2307 ) -> Result<()> {
2308 let state = self.instance_state(caller_instance).concurrent_state();
2309 let old = state.backpressure;
2310 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2311 state.backpressure = new;
2312
2313 if old > 0 && new == 0 {
2314 self.partition_pending(caller_instance)?;
2317 }
2318
2319 Ok(())
2320 }
2321
2322 fn resume_fiber(
2325 &mut self,
2326 fiber: StoreFiber<'static>,
2327 ) -> impl Future<Output = Result<()>> + '_ {
2328 let mut resuming = ResumingFiber {
2329 store: self,
2330 fiber: Some(fiber),
2331 };
2332 async move {
2333 let old_thread = resuming.store.current_thread()?;
2334 log::trace!("resume_fiber: save current thread {old_thread:?}");
2335
2336 let fiber = resuming.fiber.take().unwrap();
2337 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2338
2339 resuming.store.set_thread(old_thread)?;
2340
2341 let state = resuming.store.concurrent_state_mut()?;
2342
2343 if let Some(ot) = old_thread.guest() {
2344 state.set_thread_running(ot.thread)?;
2345 }
2346 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2347
2348 if resuming.fiber.is_some() {
2349 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2350 let reason = match state.suspend_reason.take() {
2352 Some(r) => r,
2353 None => bail_bug!("suspend reason missing when resuming fiber"),
2354 };
2355 match reason {
2356 SuspendReason::NeedWork => {
2357 if state.worker.is_none() {
2358 state.worker = resuming.fiber.take();
2359 }
2360 }
2361 SuspendReason::Yielding { thread } => {
2362 let slot = &mut state.get_mut(thread.thread)?.state;
2363 slot.check_no_fiber()?;
2364 *slot = GuestThreadState::Ready {
2365 fiber: resuming.fiber.take().unwrap(),
2366 };
2367 let instance = state.get_mut(thread.task)?.instance;
2368 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2369 }
2370 SuspendReason::ExplicitlySuspending { thread } => {
2371 let slot = &mut state.get_mut(thread.thread)?.state;
2372 slot.check_no_fiber()?;
2373 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2374 }
2375 SuspendReason::Waiting { set, thread } => {
2376 match state.get_mut(set)?.waiting.entry(thread) {
2377 Entry::Vacant(entry) => {
2378 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2379 }
2380 Entry::Occupied(_) => {
2381 bail_bug!("entry unexpectedly already exists for {thread:?}");
2382 }
2383 }
2384 }
2385 SuspendReason::YieldingToSubtask { thread } => {
2386 let instance = state.get_mut(thread.task)?.instance;
2395 if state.next_switch_item.is_some() {
2396 bail_bug!(
2399 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2400 a thread wanted to wait on a subtask"
2401 );
2402 }
2403 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2404 instance,
2405 thread,
2406 fiber: resuming.fiber.take().unwrap(),
2407 });
2408 log::trace!("set next switch item to {item:?}");
2409 }
2410 };
2411 } else {
2412 log::trace!("resume_fiber: fiber has exited");
2413 }
2414
2415 Ok(())
2416 }
2417 }
2418
2419 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2425 log::trace!("suspend fiber: {reason:?}");
2426
2427 let state = self.concurrent_state_mut()?;
2428
2429 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2436 SuspendReason::Yielding { .. }
2437 | SuspendReason::Waiting { .. }
2438 | SuspendReason::ExplicitlySuspending { .. } => {
2439 if state.switch_item.is_none() {
2442 state.take_next_switch_item()?;
2443 }
2444
2445 (true, false)
2446 }
2447 SuspendReason::YieldingToSubtask { .. } => (true, true),
2448 SuspendReason::NeedWork => (false, false),
2449 };
2450
2451 let old_next_switch_item = if save_and_restore_next_switch_item {
2452 Some(state.save_next_switch_item()?)
2453 } else {
2454 None
2455 };
2456
2457 let old_guest_thread = if save_and_restore_thread {
2458 self.current_thread()?
2459 } else {
2460 CurrentThread::None
2461 };
2462
2463 let waiting_set = match &reason {
2464 SuspendReason::Waiting { set, .. } => Some(*set),
2465 _ => None,
2466 };
2467
2468 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2469 if suspend_reason.is_some() {
2470 bail_bug!("suspend reason unexpectedly already set for store");
2471 }
2472 *suspend_reason = Some(reason);
2473
2474 if !self.fiber_async_state_mut().can_block() {
2477 return Err(format_err!("future dropped"));
2478 }
2479
2480 if let Some(set) = waiting_set {
2483 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2484 }
2485
2486 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2487
2488 if let Some(set) = waiting_set {
2489 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2490 }
2491
2492 if save_and_restore_thread {
2493 self.set_thread(old_guest_thread)?;
2494 }
2495
2496 if let Some(item) = old_next_switch_item {
2497 let state = self.concurrent_state_mut()?;
2498 state.restore_next_switch_item(item)?;
2499 }
2500
2501 Ok(())
2502 }
2503
2504 fn wait_for_event(
2505 &mut self,
2506 caller_instance: RuntimeInstance,
2507 waitable: Waitable,
2508 ) -> Result<()> {
2509 let caller = self.current_guest_thread()?;
2510 let state = self.concurrent_state_mut()?;
2511
2512 waitable.trap_if_in_waitable_set(state)?;
2513
2514 let set = state.get_mut(caller.thread)?.sync_call_set;
2515 waitable.join(state, Some(set))?;
2516
2517 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2518
2519 self.suspend(SuspendReason::Waiting {
2520 set,
2521 thread: caller,
2522 })?;
2523 let state = self.concurrent_state_mut()?;
2524
2525 waitable.join(state, None)
2526 }
2527
2528 fn cleanup_thread(
2550 &mut self,
2551 guest_thread: QualifiedThreadId,
2552 runtime_instance: RuntimeInstance,
2553 ) -> Result<()> {
2554 let state = self.concurrent_state_mut()?;
2555 state.take_next_switch_item()?;
2558 let thread_data = state.get_mut(guest_thread.thread)?;
2559 let sync_call_set = thread_data.sync_call_set;
2560 if let Some(guest_id) = thread_data.instance_rep {
2561 let (state, counter) = self.instance_state_and_handle_counter(runtime_instance);
2562 state
2563 .thread_handle_table()
2564 .guest_thread_remove(counter, guest_id)?;
2565 }
2566 let state = self.concurrent_state_mut()?;
2567
2568 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2570 if let Some(Event::Subtask {
2571 status: Status::Returned | Status::ReturnCancelled,
2572 }) = waitable.common(state)?.event
2573 {
2574 waitable.delete_from(state)?;
2575 }
2576 }
2577
2578 state.delete(guest_thread.thread)?;
2579 state.delete(sync_call_set)?;
2580 let task = state.get_mut(guest_thread.task)?;
2581 task.threads.remove(&guest_thread.thread);
2582
2583 if task.threads.is_empty() && !task.returned_or_cancelled() {
2584 bail!(Trap::NoAsyncResult);
2585 }
2586
2587 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2588 task.decremented_interesting_task_count = true;
2589
2590 debug_assert!(state.interesting_tasks > 0);
2591 state.interesting_tasks -= 1;
2592 if state.interesting_tasks == 0
2593 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2594 {
2595 waker.wake();
2596 }
2597 }
2598
2599 state.decrement_task_ref_count(guest_thread.task)?;
2600
2601 Ok(())
2602 }
2603
2604 fn cancel_guest_subtask_without_lowered_parameters(
2613 &mut self,
2614 caller_instance: RuntimeInstance,
2615 guest_task: TableId<GuestTask>,
2616 ) -> Result<()> {
2617 let concurrent_state = self.concurrent_state_mut()?;
2618 let task = concurrent_state.get_mut(guest_task)?;
2619 assert!(!task.already_lowered_parameters());
2620 task.lower_params = None;
2624 task.lift_result = None;
2625 task.exited = true;
2626 let instance = task.instance;
2627
2628 assert_eq!(1, task.threads.len());
2631 let thread = *task.threads.iter().next().unwrap();
2632 self.cleanup_thread(
2633 QualifiedThreadId {
2634 task: guest_task,
2635 thread,
2636 },
2637 caller_instance,
2638 )?;
2639
2640 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2642 let pending_count = pending.len();
2643 pending.retain(|thread, _| thread.task != guest_task);
2644
2645 if pending.len() == pending_count {
2646 let matches = |item: &WorkItem| match item {
2649 WorkItem::ResumeThread { thread, .. }
2650 | WorkItem::GuestCall {
2651 call: GuestCall { thread, .. },
2652 ..
2653 } => thread.task == guest_task,
2654 _ => false,
2655 };
2656
2657 let remove = |queue: &mut VecDeque<WorkItem>| {
2658 let count = queue.len();
2659 queue.retain(|item| !matches(item));
2660 count != queue.len()
2661 };
2662
2663 let state = self.concurrent_state_mut()?;
2664 let mut found = remove(&mut state.high_priority);
2665 found |= remove(&mut state.low_priority);
2666
2667 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2668 if matches!(item, Some(item) if matches(item)) {
2669 found = true;
2670 *item = None;
2671 }
2672 }
2673
2674 if !found {
2675 bail!(Trap::SubtaskCancelAfterTerminal);
2676 }
2677 }
2678 Ok(())
2679 }
2680
2681 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2684 if !self.concurrency_support() {
2685 return Ok(self
2686 .current_scope_id_not_concurrent()?
2687 .map(|id| CurrentScope::Id(Scope::Id(id))));
2688 }
2689
2690 Ok(match self.current_thread()? {
2691 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2692 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2693 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2694 CurrentThread::None => return Ok(None),
2695 })
2696 }
2697
2698 pub(crate) fn queue_task(
2699 &mut self,
2700 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2701 ) -> Result<()> {
2702 self.concurrent_state_mut()?
2703 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2704 Ok(())
2705 }
2706
2707 fn any_may_not_suspend(&mut self) -> Result<bool> {
2716 Ok(self
2724 .concurrent_state_mut()?
2725 .table
2726 .get_mut()
2727 .iter_mut()
2728 .filter_map(|(_, entry)| {
2729 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2730 Some(task.instance)
2731 } else {
2732 None
2733 }
2734 })
2735 .collect::<Vec<_>>()
2736 .into_iter()
2737 .any(|instance| {
2738 self.instance_state(instance)
2739 .concurrent_state()
2740 .do_not_suspend
2741 }))
2742 }
2743}
2744
2745impl Instance {
2746 fn get_event(
2749 self,
2750 store: &mut StoreOpaque,
2751 guest_task: TableId<GuestTask>,
2752 set: Option<TableId<WaitableSet>>,
2753 cancellable: bool,
2754 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2755 let state = store.concurrent_state_mut()?;
2756
2757 let task = state.get_mut(guest_task)?;
2758 let event = &mut task.event;
2759 if let Some(ev) = event
2760 && (cancellable || !matches!(ev, Event::Cancelled))
2761 {
2762 log::trace!("deliver event {ev:?} to {guest_task:?}");
2763
2764 if matches!(ev, Event::Cancelled) {
2765 task.cancel_request_delivered = true;
2766 }
2767
2768 let ev = *ev;
2769 *event = None;
2770 return Ok(Some((ev, None)));
2771 }
2772
2773 let set = match set {
2774 Some(set) => set,
2775 None => return Ok(None),
2776 };
2777 let waitable = match state.get_mut(set)?.ready.pop_first() {
2778 Some(v) => v,
2779 None => return Ok(None),
2780 };
2781
2782 let common = waitable.common(state)?;
2783 let handle = match common.handle {
2784 Some(h) => h,
2785 None => bail_bug!("handle not set when delivering event"),
2786 };
2787 let event = match common.event.take() {
2788 Some(e) => e,
2789 None => bail_bug!("event not set when delivering event"),
2790 };
2791
2792 log::trace!(
2793 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2794 );
2795
2796 waitable.on_delivery(store, self, event)?;
2797
2798 Ok(Some((event, Some((waitable, handle)))))
2799 }
2800
2801 fn handle_callback_code(
2808 self,
2809 store: &mut dyn VMStore,
2810 guest_thread: QualifiedThreadId,
2811 runtime_instance: RuntimeInstance,
2812 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2813 ) -> Result<()> {
2814 let old_thread = store.set_thread(guest_thread)?;
2815 log::trace!(
2816 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2817 );
2818
2819 store.enter_instance(runtime_instance);
2820
2821 let mut code = call(store)?;
2822
2823 if matches!(
2829 unpack_callback_code(code).0,
2830 callback_code::YIELD | callback_code::WAIT
2831 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2832 {
2833 let (unpacked, set) = unpack_callback_code(code);
2834 log::trace!(
2835 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2836 cancellation; delivering it via callback"
2837 );
2838 code = invoke_callback(store, guest_thread.task, event, 0)?;
2839 }
2840
2841 store.exit_instance(runtime_instance)?;
2842
2843 store.set_thread(old_thread)?;
2844 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2845
2846 let store = store.store_opaque_mut();
2847 let (code, set) = unpack_callback_code(code);
2848
2849 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2850
2851 let state = store.concurrent_state_mut()?;
2852
2853 state.take_next_switch_item()?;
2854
2855 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2856 let set = store
2857 .instance_state(runtime_instance)
2858 .handle_table()
2859 .waitable_set_rep(handle)?;
2860
2861 Ok(TableId::<WaitableSet>::new(set))
2862 };
2863
2864 match code {
2865 callback_code::EXIT => {
2866 log::trace!("implicit thread {guest_thread:?} completed");
2867 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2868 task.exited = true;
2869 task.callback = None;
2870
2871 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2876
2877 store.cleanup_thread(guest_thread, runtime_instance)?;
2878 }
2879 callback_code::YIELD => {
2880 let old = state
2883 .get_mut(guest_thread.thread)?
2884 .wake_on_cancel
2885 .replace(WakeOnCancel::Yielding);
2886 if !old.is_none() {
2887 bail_bug!("thread unexpectedly had wake_on_cancel set");
2888 }
2889
2890 let call = GuestCall {
2897 thread: guest_thread,
2898 kind: GuestCallKind::DeliverEvent {
2899 instance: self,
2900 set: None,
2901 },
2902 };
2903 state.push_low_priority(WorkItem::GuestCall {
2906 instance: runtime_instance,
2907 call,
2908 });
2909 }
2910 callback_code::WAIT => {
2911 let set = get_set(store, set)?;
2912 self.wait_with_callback(store, guest_thread, set)?;
2913 }
2914 _ => bail!(Trap::UnsupportedCallbackCode),
2915 }
2916
2917 Ok(())
2918 }
2919
2920 fn wait_with_callback(
2926 self,
2927 store: &mut StoreOpaque,
2928 guest_thread: QualifiedThreadId,
2929 set: TableId<WaitableSet>,
2930 ) -> Result<()> {
2931 let state = store.concurrent_state_mut()?;
2932 state.get_mut(set)?.num_waiting += 1;
2935
2936 if state.get_mut(guest_thread.task)?.event.is_some()
2937 || !state.get_mut(set)?.ready.is_empty()
2938 {
2939 let instance = state.get_mut(guest_thread.task)?.instance;
2941 state.push_high_priority(WorkItem::GuestCall {
2942 instance,
2943 call: GuestCall {
2944 thread: guest_thread,
2945 kind: GuestCallKind::DeliverEvent {
2946 instance: self,
2947 set: Some(set),
2948 },
2949 },
2950 });
2951 return Ok(());
2952 }
2953
2954 let instance = store
2960 .concurrent_state_mut()?
2961 .get_mut(guest_thread.task)?
2962 .instance;
2963 store.switch_or_trap_if_may_not_suspend(instance)?;
2964
2965 let state = store.concurrent_state_mut()?;
2971 let old = state
2972 .get_mut(guest_thread.thread)?
2973 .wake_on_cancel
2974 .replace(WakeOnCancel::Waiting(set));
2975 if !old.is_none() {
2976 bail_bug!("thread unexpectedly had wake_on_cancel set");
2977 }
2978 let old = state
2979 .get_mut(set)?
2980 .waiting
2981 .insert(guest_thread, WaitMode::Callback(self));
2982 if !old.is_none() {
2983 bail_bug!("set's waiting set already had this thread registered");
2984 }
2985 Ok(())
2986 }
2987
2988 unsafe fn stage_call<T: 'static>(
2995 self,
2996 mut store: StoreContextMut<T>,
2997 guest_thread: QualifiedThreadId,
2998 callee: SendSyncPtr<VMFuncRef>,
2999 param_count: usize,
3000 result_count: usize,
3001 async_: bool,
3002 callback: Option<SendSyncPtr<VMFuncRef>>,
3003 post_return: Option<SendSyncPtr<VMFuncRef>>,
3004 host_caller: bool,
3005 ) -> Result<()> {
3006 unsafe fn make_call<T: 'static>(
3021 store: StoreContextMut<T>,
3022 guest_thread: QualifiedThreadId,
3023 callee: SendSyncPtr<VMFuncRef>,
3024 param_count: usize,
3025 result_count: usize,
3026 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3027 + Send
3028 + Sync
3029 + 'static
3030 + use<T> {
3031 let token = StoreToken::new(store);
3032 move |store: &mut dyn VMStore| {
3033 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3034
3035 store
3036 .concurrent_state_mut()?
3037 .set_thread_running(guest_thread.thread)?;
3038 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3039 let lower = match task.lower_params.take() {
3040 Some(l) => l,
3041 None => bail_bug!("lower_params missing"),
3042 };
3043
3044 lower(store, &mut storage[..param_count])?;
3045
3046 let mut store = token.as_context_mut(store);
3047
3048 unsafe {
3051 crate::Func::call_unchecked_raw(
3052 &mut store,
3053 callee.as_non_null(),
3054 NonNull::new(
3055 &mut storage[..param_count.max(result_count)]
3056 as *mut [MaybeUninit<ValRaw>] as _,
3057 )
3058 .unwrap(),
3059 UncaughtException::Trap,
3060 )?;
3061 }
3062
3063 Ok(storage)
3064 }
3065 }
3066
3067 let call = unsafe {
3071 make_call(
3072 store.as_context_mut(),
3073 guest_thread,
3074 callee,
3075 param_count,
3076 result_count,
3077 )
3078 };
3079
3080 let callee_instance = store
3081 .0
3082 .concurrent_state_mut()?
3083 .get_mut(guest_thread.task)?
3084 .instance;
3085
3086 let fun = if callback.is_some() {
3087 assert!(async_);
3088
3089 Box::new(move |store: &mut dyn VMStore| {
3090 self.add_guest_thread_to_instance_table(
3091 guest_thread.thread,
3092 store,
3093 callee_instance.index,
3094 )?;
3095 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3096 let storage = call(store)?;
3097
3098 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3101 })
3102 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3103 } else {
3104 let token = StoreToken::new(store.as_context_mut());
3105 Box::new(move |store: &mut dyn VMStore| {
3106 self.add_guest_thread_to_instance_table(
3107 guest_thread.thread,
3108 store,
3109 callee_instance.index,
3110 )?;
3111 let old_thread = store.set_thread(guest_thread)?;
3112 log::trace!(
3113 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3114 );
3115 let flags = self.id().get(store).instance_flags(callee_instance.index);
3116
3117 let callee_async_typed = store
3118 .concurrent_state_mut()?
3119 .get_mut(guest_thread.task)?
3120 .async_typed;
3121
3122 if !async_ && callee_async_typed {
3126 store.enter_instance(callee_instance);
3127 }
3128
3129 if !callee_async_typed {
3130 store.enter_sync_call(callee_instance)?;
3131 }
3132
3133 let storage = call(store)?;
3140
3141 if !callee_async_typed {
3142 store.exit_sync_call(callee_instance)?;
3143 }
3144
3145 if !async_ {
3146 if callee_async_typed {
3152 store.exit_instance(callee_instance)?;
3153 }
3154
3155 let lift = {
3156 let state = store.concurrent_state_mut()?;
3157 if !state.get_mut(guest_thread.task)?.result.is_none() {
3158 bail_bug!("task has already produced a result");
3159 }
3160
3161 match state.get_mut(guest_thread.task)?.lift_result.take() {
3162 Some(lift) => lift,
3163 None => bail_bug!("lift_result field is missing"),
3164 }
3165 };
3166
3167 let result = (lift.lift)(store, unsafe {
3170 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3171 &storage[..result_count],
3172 )
3173 })?;
3174
3175 let post_return_arg = match result_count {
3176 0 => ValRaw::i32(0),
3177 1 => unsafe { storage[0].assume_init() },
3180 _ => unreachable!(),
3181 };
3182
3183 unsafe {
3184 call_post_return(
3185 token.as_context_mut(store),
3186 post_return.map(|v| v.as_non_null()),
3187 post_return_arg,
3188 flags,
3189 )?;
3190 }
3191
3192 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3193 }
3194
3195 store.set_thread(old_thread)?;
3196
3197 store
3198 .concurrent_state_mut()?
3199 .get_mut(guest_thread.task)?
3200 .exited = true;
3201
3202 log::trace!(
3203 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3204 );
3205
3206 if callee_async_typed {
3207 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3212 }
3213
3214 store.cleanup_thread(guest_thread, callee_instance)?;
3216 Ok(())
3217 })
3218 };
3219
3220 store.0.concurrent_state_mut()?.push_work_item(
3221 WorkItem::GuestCall {
3222 instance: callee_instance,
3223 call: GuestCall {
3224 thread: guest_thread,
3225 kind: GuestCallKind::StartImplicit(fun),
3226 },
3227 },
3228 if host_caller {
3229 Priority::High
3230 } else {
3231 Priority::Switch
3232 },
3233 )?;
3234
3235 Ok(())
3236 }
3237
3238 unsafe fn prepare_call<T: 'static>(
3251 self,
3252 mut store: StoreContextMut<T>,
3253 start: NonNull<VMFuncRef>,
3254 return_: NonNull<VMFuncRef>,
3255 caller_instance: RuntimeComponentInstanceIndex,
3256 callee_instance: RuntimeComponentInstanceIndex,
3257 task_return_type: TypeTupleIndex,
3258 callee_async_typed: bool,
3259 memory: *mut VMMemoryDefinition,
3260 string_encoding: StringEncoding,
3261 caller_info: CallerInfo,
3262 ) -> Result<()> {
3263 enum ResultInfo {
3264 Heap { results: u32 },
3265 Stack { result_count: u32 },
3266 }
3267
3268 let result_info = match &caller_info {
3269 CallerInfo::Async {
3270 has_result: true,
3271 params,
3272 } => ResultInfo::Heap {
3273 results: match params.last() {
3275 Some(r) => r.get_u32(),
3276 None => bail_bug!("retptr missing"),
3277 },
3278 },
3279 CallerInfo::Async {
3280 has_result: false, ..
3281 } => ResultInfo::Stack { result_count: 0 },
3282 CallerInfo::Sync {
3283 result_count,
3284 params,
3285 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3286 results: match params.last() {
3288 Some(r) => r.get_u32(),
3289 None => bail_bug!("arg ptr missing"),
3290 },
3291 },
3292 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3293 result_count: *result_count,
3294 },
3295 };
3296
3297 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3298
3299 let start = SendSyncPtr::new(start);
3303 let return_ = SendSyncPtr::new(return_);
3304 let token = StoreToken::new(store.as_context_mut());
3305 let old_thread = store.0.current_guest_thread()?;
3306
3307 let state = store.0.concurrent_state_mut()?;
3308
3309 debug_assert_eq!(
3310 state.get_mut(old_thread.task)?.instance,
3311 self.runtime_instance(caller_instance)
3312 );
3313
3314 let guest_thread = GuestTask::new(
3315 state,
3316 Box::new(move |store, dst| {
3317 let mut store = token.as_context_mut(store);
3318 assert!(dst.len() <= MAX_FLAT_PARAMS);
3319 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3321 let count = match caller_info {
3322 CallerInfo::Async { params, has_result } => {
3326 let params = ¶ms[..params.len() - usize::from(has_result)];
3327 for (param, src) in params.iter().zip(&mut src) {
3328 src.write(*param);
3329 }
3330 params.len()
3331 }
3332
3333 CallerInfo::Sync { params, .. } => {
3335 for (param, src) in params.iter().zip(&mut src) {
3336 src.write(*param);
3337 }
3338 params.len()
3339 }
3340 };
3341 unsafe {
3348 crate::Func::call_unchecked_raw(
3349 &mut store,
3350 start.as_non_null(),
3351 NonNull::new(
3352 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3353 )
3354 .unwrap(),
3355 UncaughtException::Trap,
3356 )?;
3357 }
3358 dst.copy_from_slice(&src[..dst.len()]);
3359 let task = store.0.current_guest_thread()?.task;
3360 let state = store.0.concurrent_state_mut()?;
3361 Waitable::Guest(task).set_event(
3362 state,
3363 Some(Event::Subtask {
3364 status: Status::Started,
3365 }),
3366 )?;
3367 Ok(())
3368 }),
3369 LiftResult {
3370 lift: Box::new(move |store, src| {
3371 let mut store = token.as_context_mut(store);
3374 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3376 my_src.push(ValRaw::u32(*results));
3377 }
3378
3379 unsafe {
3386 crate::Func::call_unchecked_raw(
3387 &mut store,
3388 return_.as_non_null(),
3389 my_src.as_mut_slice().into(),
3390 UncaughtException::Trap,
3391 )?;
3392 }
3393
3394 let thread = store.0.current_guest_thread()?;
3395 let state = store.0.concurrent_state_mut()?;
3396 if sync_caller {
3397 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3398 if let ResultInfo::Stack { result_count } = &result_info {
3399 match result_count {
3400 0 => None,
3401 1 => Some(my_src[0]),
3402 _ => unreachable!(),
3403 }
3404 } else {
3405 None
3406 },
3407 );
3408 }
3409 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3410 }),
3411 ty: task_return_type,
3412 memory: NonNull::new(memory).map(SendSyncPtr::new),
3413 string_encoding,
3414 },
3415 Caller::Guest { thread: old_thread },
3416 None,
3417 self.runtime_instance(callee_instance),
3418 callee_async_typed,
3419 false,
3422 )?;
3423
3424 store.0.set_thread(guest_thread)?;
3427 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3428
3429 Ok(())
3430 }
3431
3432 unsafe fn call_callback<T>(
3437 self,
3438 mut store: StoreContextMut<T>,
3439 function: SendSyncPtr<VMFuncRef>,
3440 event: Event,
3441 handle: u32,
3442 ) -> Result<u32> {
3443 let (ordinal, result) = event.parts();
3444 let params = &mut [
3445 ValRaw::u32(ordinal),
3446 ValRaw::u32(handle),
3447 ValRaw::u32(result),
3448 ];
3449 unsafe {
3454 crate::Func::call_unchecked_raw(
3455 &mut store,
3456 function.as_non_null(),
3457 params.as_mut_slice().into(),
3458 UncaughtException::Trap,
3459 )?;
3460 }
3461 Ok(params[0].get_u32())
3462 }
3463
3464 unsafe fn start_call<T: 'static>(
3482 self,
3483 mut store: StoreContextMut<T>,
3484 callback: *mut VMFuncRef,
3485 post_return: *mut VMFuncRef,
3486 callee: NonNull<VMFuncRef>,
3487 param_count: u32,
3488 result_count: u32,
3489 flags: u32,
3490 storage: &mut [MaybeUninit<ValRaw>],
3491 ) -> Result<()> {
3492 let token = StoreToken::new(store.as_context_mut());
3493 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3494 let guest_thread = store.0.current_guest_thread()?;
3495 let state = store.0.concurrent_state_mut()?;
3496
3497 if !state.event_loop_running {
3498 bail_bug!("Instance::start_call called without a running event loop");
3499 }
3500
3501 let callee = SendSyncPtr::new(callee);
3502 let param_count = usize::try_from(param_count)?;
3503 assert!(param_count <= MAX_FLAT_PARAMS);
3504 let result_count = usize::try_from(result_count)?;
3505 assert!(result_count <= MAX_FLAT_RESULTS);
3506
3507 let task = state.get_mut(guest_thread.task)?;
3508 let callee_async_typed = task.async_typed;
3509 let callee_instance = task.instance;
3510
3511 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3512
3513 if let Some(callback) = NonNull::new(callback) {
3514 let callback = SendSyncPtr::new(callback);
3518 task.callback = Some(Box::new(move |store, event, handle| {
3519 let store = token.as_context_mut(store);
3520 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3521 }));
3522 }
3523
3524 let Caller::Guest { thread: caller } = &task.caller else {
3525 bail_bug!("start_call unexpectedly invoked for host->guest call");
3528 };
3529 let caller = *caller;
3530 let caller_instance = state.get_mut(caller.task)?.instance;
3531
3532 unsafe {
3534 self.stage_call(
3535 store.as_context_mut(),
3536 guest_thread,
3537 callee,
3538 param_count,
3539 result_count,
3540 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3541 NonNull::new(callback).map(SendSyncPtr::new),
3542 NonNull::new(post_return).map(SendSyncPtr::new),
3543 false,
3544 )?;
3545 }
3546
3547 let old_do_not_suspend = if callee_async_typed {
3548 let state = store.0.instance_state(callee_instance).concurrent_state();
3555 let old_do_not_suspend = state.do_not_suspend;
3556 state.do_not_suspend = false;
3557 Some(old_do_not_suspend)
3558 } else {
3559 None
3560 };
3561
3562 let state = store.0.concurrent_state_mut()?;
3563
3564 let guest_waitable = Waitable::Guest(guest_thread.task);
3567 let old_set = guest_waitable.common(state)?.set;
3568 let set = state.get_mut(caller.thread)?.sync_call_set;
3569 guest_waitable.join(state, Some(set))?;
3570
3571 store.0.set_thread(CurrentThread::None)?;
3572
3573 let mut yielded = false;
3589 let (status, waitable) = loop {
3590 store.0.suspend(if yielded {
3591 SuspendReason::Waiting {
3592 set,
3593 thread: caller,
3594 }
3595 } else {
3596 yielded = true;
3597 SuspendReason::YieldingToSubtask { thread: caller }
3598 })?;
3599
3600 if let Some(old_do_not_suspend) = old_do_not_suspend {
3601 store
3602 .0
3603 .instance_state(callee_instance)
3604 .concurrent_state()
3605 .do_not_suspend = old_do_not_suspend;
3606 }
3607
3608 let state = store.0.concurrent_state_mut()?;
3609
3610 log::trace!("taking event for {:?}", guest_thread.task);
3611 let event = guest_waitable.take_event(state)?;
3612 let Some(Event::Subtask { status }) = event else {
3613 bail_bug!("subtasks should only get subtask events, got {event:?}")
3614 };
3615
3616 log::trace!("status {status:?} for {:?}", guest_thread.task);
3617
3618 if status == Status::Returned {
3619 break (status, None);
3621 } else if async_caller {
3622 let (state, counter) = store.0.instance_state_and_handle_counter(caller_instance);
3626 let handle = state
3627 .handle_table()
3628 .subtask_insert_guest(counter, guest_thread.task.rep())?;
3629 store
3630 .0
3631 .concurrent_state_mut()?
3632 .get_mut(guest_thread.task)?
3633 .common
3634 .handle = Some(handle);
3635 break (status, Some(handle));
3636 } else {
3637 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3641 }
3642 };
3643
3644 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3645
3646 store.0.set_thread(caller)?;
3648
3649 store
3650 .0
3651 .concurrent_state_mut()?
3652 .set_thread_running(caller.thread)?;
3653
3654 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3655
3656 if async_caller {
3657 let Some(slot) = storage.first_mut() else {
3658 bail_bug!("no storage for async call status");
3659 };
3660 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3661 } else {
3662 let state = store.0.concurrent_state_mut()?;
3665 let task = state.get_mut(guest_thread.task)?;
3666 if let Some(Some(result)) = task.sync_result.take()? {
3667 storage[0] = MaybeUninit::new(result);
3668 }
3669 }
3670
3671 if status == Status::Returned {
3672 store
3673 .0
3674 .concurrent_state_mut()?
3675 .decrement_task_ref_count(guest_thread.task)?;
3676 }
3677
3678 Ok(())
3679 }
3680
3681 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3697 self,
3698 mut store: StoreContextMut<'_, T>,
3699 host_task: EnteredHostTask,
3700 result_may_require_realloc: bool,
3701 future: impl Future<Output = Result<R>> + Send + 'static,
3702 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3703 + Send
3704 + 'static,
3705 ) -> Result<u32> {
3706 let token = StoreToken::new(store.as_context_mut());
3707
3708 let (join_handle, future) = JoinHandle::run(future);
3711 let mut future = Box::pin(future);
3712
3713 let poll = tls::set(store.0, || {
3718 future
3719 .as_mut()
3720 .poll(&mut Context::from_waker(&Waker::noop()))
3721 });
3722
3723 match poll {
3724 Poll::Ready(result) => {
3726 let result = result.transpose()?;
3727 let task = store.0.current_materialized_host_task()?;
3730 lower(store.as_context_mut(), result, true, task)?;
3731 return Ok(Status::Returned.pack(None));
3732 }
3733
3734 Poll::Pending => {}
3736 }
3737
3738 let Some(task) = store.0.materialize_host_task_id()? else {
3742 bail_bug!("current thread is not a host thread")
3743 };
3744 {
3745 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3746 assert!(matches!(state, HostTaskState::CalleeStarted));
3747 *state = HostTaskState::CalleeRunning(join_handle);
3748 }
3749
3750 let future = Box::pin(async move {
3758 let result = match run_with_host_task_set(task, future).await? {
3759 Some(result) => Some(result?),
3760 None => None,
3761 };
3762 let on_complete = move |store: &mut dyn VMStore| {
3763 let mut store = token.as_context_mut(store);
3767 let old = store.0.set_thread(task)?;
3768
3769 let status = if result.is_some() {
3770 Status::Returned
3771 } else {
3772 Status::ReturnCancelled
3773 };
3774
3775 lower(store.as_context_mut(), result, false, Some(task))?;
3776 let state = store.0.concurrent_state_mut()?;
3777 match &mut state.get_mut(task)?.state {
3778 pending @ HostTaskState::CalleeCancelling => {
3781 *pending = HostTaskState::CalleeDone { cancelled: true };
3782 }
3783
3784 other => *other = HostTaskState::CalleeDone { cancelled: false },
3786 }
3787 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3788
3789 store.0.set_thread(old)?;
3790 Ok(())
3791 };
3792
3793 tls::get(move |store| {
3794 if result_may_require_realloc {
3795 store
3800 .concurrent_state_mut()?
3801 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3802 on_complete,
3803 ))));
3804 Ok(())
3805 } else {
3806 on_complete(store)
3809 }
3810 })
3811 });
3812
3813 let caller = match host_task {
3816 Some(caller) => caller,
3817 None => bail_bug!("host task wasn't created but should have been"),
3818 };
3819 let state = store.0.concurrent_state_mut()?;
3820 state.push_future(future);
3821 let instance = state.get_mut(caller.task)?.instance;
3822 let (state, counter) = store.0.instance_state_and_handle_counter(instance);
3823 let handle = state
3824 .handle_table()
3825 .subtask_insert_host(counter, task.rep())?;
3826 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3827 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3828
3829 store.0.set_thread(caller)?;
3833 Ok(Status::Started.pack(Some(handle)))
3834 }
3835
3836 pub(crate) fn task_return(
3839 self,
3840 store: &mut dyn VMStore,
3841 ty: TypeTupleIndex,
3842 options: OptionsIndex,
3843 storage: &[ValRaw],
3844 ) -> Result<()> {
3845 let guest_thread = store.current_guest_thread()?;
3846 let state = store.concurrent_state_mut()?;
3847 if !state.get_mut(guest_thread.task)?.async_lifted {
3848 bail!(Trap::TaskReturnOrCancelSyncLifted);
3849 }
3850 let lift = state
3851 .get_mut(guest_thread.task)?
3852 .lift_result
3853 .take()
3854 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3855 if !state.get_mut(guest_thread.task)?.result.is_none() {
3856 bail_bug!("task result unexpectedly already set");
3857 }
3858
3859 let CanonicalOptions {
3860 string_encoding,
3861 data_model,
3862 ..
3863 } = &self.id().get(store).component().env_component().options[options];
3864
3865 let invalid = ty != lift.ty
3866 || string_encoding != &lift.string_encoding
3867 || match data_model {
3868 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3869 Some(memory) => {
3870 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3871 let actual = self.id().get(store).runtime_memory(memory);
3872 expected != actual.as_ptr()
3873 }
3874 None => false,
3877 },
3878 CanonicalOptionsDataModel::Gc { .. } => true,
3880 };
3881
3882 if invalid {
3883 bail!(Trap::TaskReturnInvalid);
3884 }
3885
3886 log::trace!("task.return for {guest_thread:?}");
3887
3888 let result = (lift.lift)(store, storage)?;
3889 self.task_complete(store, guest_thread.task, result, Status::Returned)
3890 }
3891
3892 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3894 let guest_thread = store.current_guest_thread()?;
3895 let state = store.concurrent_state_mut()?;
3896 let task = state.get_mut(guest_thread.task)?;
3897 if !task.async_lifted {
3898 bail!(Trap::TaskReturnOrCancelSyncLifted);
3899 }
3900 if !task.cancel_request_delivered {
3901 bail!(Trap::TaskCancelNotCancelled);
3902 }
3903 _ = task
3904 .lift_result
3905 .take()
3906 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3907
3908 if !task.result.is_none() {
3909 bail_bug!("task result should not bet set yet");
3910 }
3911
3912 log::trace!("task.cancel for {guest_thread:?}");
3913
3914 self.task_complete(
3915 store,
3916 guest_thread.task,
3917 Box::new(DummyResult),
3918 Status::ReturnCancelled,
3919 )
3920 }
3921
3922 fn task_complete(
3928 self,
3929 store: &mut StoreOpaque,
3930 guest_task: TableId<GuestTask>,
3931 result: Box<dyn Any + Send + Sync>,
3932 status: Status,
3933 ) -> Result<()> {
3934 store
3935 .component_resource_tables(Some(self))?
3936 .validate_scope_exit()?;
3937
3938 let state = store.concurrent_state_mut()?;
3939 let task = state.get_mut(guest_task)?;
3940
3941 task.event = None;
3945
3946 if let Caller::Host { tx, .. } = &mut task.caller {
3947 if let Some(tx) = tx.take() {
3948 _ = tx.send(result);
3949 }
3950 } else {
3951 task.result = Some(result);
3952 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3953 }
3954
3955 Ok(())
3956 }
3957
3958 pub(crate) fn waitable_set_new(
3960 self,
3961 store: &mut StoreOpaque,
3962 caller_instance: RuntimeComponentInstanceIndex,
3963 ) -> Result<u32> {
3964 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3965 let (state, counter) =
3966 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3967 let handle = state
3968 .handle_table()
3969 .waitable_set_insert(counter, set.rep())?;
3970 log::trace!("new waitable set {set:?} (handle {handle})");
3971 Ok(handle)
3972 }
3973
3974 pub(crate) fn waitable_set_drop(
3976 self,
3977 store: &mut StoreOpaque,
3978 caller_instance: RuntimeComponentInstanceIndex,
3979 set: u32,
3980 ) -> Result<()> {
3981 let (state, counter) =
3982 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
3983 let rep = state.handle_table().waitable_set_remove(counter, set)?;
3984
3985 log::trace!("drop waitable set {rep} (handle {set})");
3986
3987 let set = store
3991 .concurrent_state_mut()?
3992 .get_mut(TableId::<WaitableSet>::new(rep))?;
3993 if set.num_waiting > 0 {
3994 bail!(Trap::WaitableSetDropHasWaiters);
3995 }
3996
3997 store
3998 .concurrent_state_mut()?
3999 .delete(TableId::<WaitableSet>::new(rep))?;
4000
4001 Ok(())
4002 }
4003
4004 pub(crate) fn waitable_join(
4006 self,
4007 store: &mut StoreOpaque,
4008 caller_instance: RuntimeComponentInstanceIndex,
4009 waitable_handle: u32,
4010 set_handle: u32,
4011 ) -> Result<()> {
4012 let mut instance = self.id().get_mut(store);
4013 let waitable =
4014 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4015
4016 let set = if set_handle == 0 {
4017 None
4018 } else {
4019 let set = instance.instance_states().0[caller_instance]
4020 .handle_table()
4021 .waitable_set_rep(set_handle)?;
4022
4023 Some(TableId::<WaitableSet>::new(set))
4024 };
4025
4026 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4027
4028 log::trace!(
4029 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4030 );
4031
4032 waitable.join(store.concurrent_state_mut()?, set)
4033 }
4034
4035 pub(crate) fn subtask_drop(
4037 self,
4038 store: &mut StoreOpaque,
4039 caller_instance: RuntimeComponentInstanceIndex,
4040 task_id: u32,
4041 ) -> Result<()> {
4042 self.waitable_join(store, caller_instance, task_id, 0)?;
4043
4044 let (state, counter) =
4045 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
4046 let (rep, is_host) = state.handle_table().subtask_remove(counter, task_id)?;
4047
4048 let concurrent_state = store.concurrent_state_mut()?;
4049 let waitable = if is_host {
4050 let id = TableId::<HostTask>::new(rep);
4051 let task = concurrent_state.get_mut(id)?;
4052 match &task.state {
4053 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4054 bail!(Trap::SubtaskDropNotResolved)
4055 }
4056 HostTaskState::CalleeDone { .. } => {}
4057 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4058 bail_bug!("invalid state for callee in `subtask.drop`")
4059 }
4060 }
4061
4062 Waitable::Host(id)
4063 } else {
4064 let id = TableId::<GuestTask>::new(rep);
4065 let task = concurrent_state.get_mut(id)?;
4066 if task.lift_result.is_some() {
4067 bail!(Trap::SubtaskDropNotResolved);
4068 }
4069 Waitable::Guest(id)
4070 };
4071
4072 waitable.common(concurrent_state)?.handle = None;
4073
4074 if waitable.take_event(concurrent_state)?.is_some() {
4077 bail!(Trap::SubtaskDropNotResolved);
4078 }
4079
4080 waitable.delete_from(concurrent_state)?;
4081
4082 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4083 Ok(())
4084 }
4085
4086 pub(crate) fn waitable_set_wait(
4088 self,
4089 store: &mut StoreOpaque,
4090 options: OptionsIndex,
4091 set: u32,
4092 payload: u32,
4093 ) -> Result<u32> {
4094 let &CanonicalOptions {
4095 instance: caller_instance,
4096 ..
4097 } = &self.id().get(store).component().env_component().options[options];
4098 let caller = self.runtime_instance(caller_instance);
4099 let rep = store
4100 .instance_state(self.runtime_instance(caller_instance))
4101 .handle_table()
4102 .waitable_set_rep(set)?;
4103
4104 self.waitable_check(
4105 store,
4106 caller,
4107 WaitableCheck::Wait,
4108 WaitableCheckParams {
4109 set: TableId::new(rep),
4110 options,
4111 payload,
4112 },
4113 )
4114 }
4115
4116 pub(crate) fn waitable_set_poll(
4118 self,
4119 store: &mut StoreOpaque,
4120 options: OptionsIndex,
4121 set: u32,
4122 payload: u32,
4123 ) -> Result<u32> {
4124 let &CanonicalOptions {
4125 instance: caller_instance,
4126 ..
4127 } = &self.id().get(store).component().env_component().options[options];
4128 let caller = self.runtime_instance(caller_instance);
4129 let rep = store
4130 .instance_state(caller)
4131 .handle_table()
4132 .waitable_set_rep(set)?;
4133
4134 self.waitable_check(
4135 store,
4136 caller,
4137 WaitableCheck::Poll,
4138 WaitableCheckParams {
4139 set: TableId::new(rep),
4140 options,
4141 payload,
4142 },
4143 )
4144 }
4145
4146 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4148 let thread_id = store.current_guest_thread()?.thread;
4149 match store
4150 .concurrent_state_mut()?
4151 .get_mut(thread_id)?
4152 .instance_rep
4153 {
4154 Some(r) => Ok(r),
4155 None => bail_bug!("thread should have instance_rep by now"),
4156 }
4157 }
4158
4159 pub(crate) fn thread_new_indirect<T: 'static>(
4161 self,
4162 mut store: StoreContextMut<T>,
4163 runtime_instance: RuntimeComponentInstanceIndex,
4164 start_func_ty: ModuleInternedTypeIndex,
4165 start_func_table_idx: RuntimeTableIndex,
4166 start_func_idx: u32,
4167 context: i32,
4168 ) -> Result<u32> {
4169 log::trace!("creating new thread");
4170
4171 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4172 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4173 else {
4174 bail_bug!("thread.new-indirect start function type should be registered");
4175 };
4176 let callee = instance
4177 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4178 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4179 if callee.type_index(store.0) != start_func_ty {
4180 bail!(Trap::ThreadNewIndirectInvalidType);
4181 }
4182
4183 let token = StoreToken::new(store.as_context_mut());
4184 let start_func = Box::new(
4185 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4186 let old_thread = store.set_thread(guest_thread)?;
4187 log::trace!(
4188 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4189 );
4190
4191 let mut store = token.as_context_mut(store);
4192 let mut params = [ValRaw::i32(context)];
4193 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4196
4197 store.0.set_thread(old_thread)?;
4198
4199 let runtime_instance = self.runtime_instance(runtime_instance);
4200
4201 store
4204 .0
4205 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4206
4207 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4208
4209 log::trace!("explicit thread {guest_thread:?} completed");
4210 let state = store.0.concurrent_state_mut()?;
4211 if let Some(t) = old_thread.guest() {
4212 state.set_thread_running(t.thread)?;
4213 }
4214 log::trace!("thread start: restored {old_thread:?} as current thread");
4215
4216 Ok(())
4217 },
4218 );
4219
4220 let current_thread = store.0.current_guest_thread()?;
4221 let state = store.0.concurrent_state_mut()?;
4222 let parent_task = current_thread.task;
4223
4224 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4225 let thread_id = state.push(new_thread)?;
4226 state.get_mut(parent_task)?.threads.insert(thread_id);
4227
4228 log::trace!("new thread with id {thread_id:?} created");
4229
4230 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4231 }
4232
4233 pub(crate) fn resume_thread(
4234 self,
4235 store: &mut StoreOpaque,
4236 runtime_instance: RuntimeComponentInstanceIndex,
4237 thread_idx: u32,
4238 how: ResumeThread,
4239 ) -> Result<bool> {
4240 let thread_id =
4241 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4242 let state = store.concurrent_state_mut()?;
4243 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4244
4245 if store.current_guest_thread()? == guest_thread {
4246 bail!(Trap::CannotResumeThread);
4247 }
4248
4249 let priority = match how {
4250 ResumeThread::Resume => Priority::Switch,
4251 ResumeThread::ResumeLater => Priority::Low,
4252
4253 ResumeThread::Promote => {
4256 let instance = self.runtime_instance(runtime_instance);
4257 let do_not_enter = store
4258 .instance_state(instance)
4259 .concurrent_state()
4260 .do_not_enter;
4261 return store.concurrent_state_mut()?.promote_work_item_matching(
4262 |item: &WorkItem| match item {
4263 WorkItem::ResumeThread { thread, .. }
4264 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4265
4266 WorkItem::GuestCall {
4267 call: GuestCall { thread, kind },
4268 ..
4269 } => {
4270 *thread == guest_thread
4271 && match kind {
4272 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4276
4277 GuestCallKind::StartExplicit(_) => true,
4280
4281 GuestCallKind::StartImplicit(_) => false,
4285 }
4286 }
4287
4288 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4289 },
4290 );
4291 }
4292 };
4293
4294 let state = store.concurrent_state_mut()?;
4295 let thread = state.get_mut(guest_thread.thread)?;
4296
4297 match &thread.state {
4300 GuestThreadState::NotStartedExplicit(_) => {}
4301 GuestThreadState::Suspended(_) => {
4302 log::trace!("resuming thread {thread_id:?} that was suspended");
4303 state.schedule_suspended_thread(
4304 guest_thread,
4305 self.runtime_instance(runtime_instance),
4306 priority,
4307 )?;
4308 return Ok(true);
4309 }
4310 _ => bail!(Trap::CannotResumeThread),
4311 }
4312 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4313 GuestThreadState::NotStartedExplicit(start_func) => {
4314 log::trace!("starting thread {guest_thread:?}");
4315 let guest_call = WorkItem::GuestCall {
4316 instance: self.runtime_instance(runtime_instance),
4317 call: GuestCall {
4318 thread: guest_thread,
4319 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4320 start_func(store, guest_thread)
4321 })),
4322 },
4323 };
4324 state.push_work_item(guest_call, priority)?;
4325 }
4326 other => {
4327 thread.state = other;
4328 bail_bug!("thread state checked to be resumable above");
4329 }
4330 }
4331 Ok(true)
4332 }
4333
4334 fn add_guest_thread_to_instance_table(
4335 self,
4336 thread_id: TableId<GuestThread>,
4337 store: &mut StoreOpaque,
4338 runtime_instance: RuntimeComponentInstanceIndex,
4339 ) -> Result<u32> {
4340 let (state, counter) =
4341 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4342 let guest_id = state
4343 .thread_handle_table()
4344 .guest_thread_insert(counter, thread_id.rep())?;
4345 store
4346 .concurrent_state_mut()?
4347 .get_mut(thread_id)?
4348 .instance_rep = Some(guest_id);
4349 Ok(guest_id)
4350 }
4351
4352 pub(crate) fn suspension_intrinsic(
4356 self,
4357 store: &mut StoreOpaque,
4358 caller: RuntimeComponentInstanceIndex,
4359 yielding: bool,
4360 to_thread: SuspensionTarget,
4361 ) -> Result<WaitResult> {
4362 let check_suspend = match to_thread {
4363 SuspensionTarget::Promote(thread) => {
4364 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4365 }
4366 SuspensionTarget::Resume(thread) => {
4367 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4368 bail_bug!(
4369 "`resume_thread` should only ever return false \
4370 when `ResumeThread::Promote` is passed to it"
4371 );
4372 }
4373 false
4374 }
4375 SuspensionTarget::None => true,
4376 };
4377
4378 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4379 return if yielding {
4380 Ok(WaitResult::Completed)
4381 } else {
4382 Err(Trap::CannotBlockSyncTask.into())
4383 };
4384 }
4385
4386 let guest_thread = store.current_guest_thread()?;
4387
4388 let reason = if yielding {
4389 SuspendReason::Yielding {
4390 thread: guest_thread,
4391 }
4392 } else {
4393 SuspendReason::ExplicitlySuspending {
4394 thread: guest_thread,
4395 }
4396 };
4397
4398 store.suspend(reason)?;
4399
4400 Ok(WaitResult::Completed)
4401 }
4402
4403 fn waitable_check(
4405 self,
4406 store: &mut StoreOpaque,
4407 caller: RuntimeInstance,
4408 check: WaitableCheck,
4409 params: WaitableCheckParams,
4410 ) -> Result<u32> {
4411 let guest_thread = store.current_guest_thread()?;
4412
4413 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4414
4415 match &check {
4418 WaitableCheck::Wait => {
4419 let set = params.set;
4420
4421 loop {
4426 let state = store.concurrent_state_mut()?;
4427 let task = state.get_mut(guest_thread.task)?;
4428 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4429 || !state.get_mut(set)?.ready.is_empty()
4430 {
4431 break;
4432 }
4433
4434 store.switch_or_trap_if_may_not_suspend(caller)?;
4435
4436 store.suspend(SuspendReason::Waiting {
4437 set,
4438 thread: guest_thread,
4439 })?;
4440 }
4441 }
4442 WaitableCheck::Poll => {}
4443 }
4444
4445 log::trace!(
4446 "waitable check for {guest_thread:?}; set {:?}, part two",
4447 params.set
4448 );
4449
4450 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4452
4453 let (ordinal, handle, result) = match &check {
4454 WaitableCheck::Wait => {
4455 let (event, waitable) = match event {
4456 Some(p) => p,
4457 None => bail_bug!("event expected to be present"),
4458 };
4459 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4460 let (ordinal, result) = event.parts();
4461 (ordinal, handle, result)
4462 }
4463 WaitableCheck::Poll => {
4464 if let Some((event, waitable)) = event {
4465 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4466 let (ordinal, result) = event.parts();
4467 (ordinal, handle, result)
4468 } else {
4469 log::trace!(
4470 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4471 guest_thread.task,
4472 params.set
4473 );
4474 let (ordinal, result) = Event::None.parts();
4475 (ordinal, 0, result)
4476 }
4477 }
4478 };
4479 let memory = self.options_memory_mut(store, params.options);
4480 let ptr = crate::component::func::validate_inbounds_dynamic(
4481 &CanonicalAbiInfo::POINTER_PAIR,
4482 memory,
4483 &ValRaw::u32(params.payload),
4484 )?;
4485 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4486 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4487 Ok(ordinal)
4488 }
4489
4490 pub(crate) fn subtask_cancel(
4492 self,
4493 store: &mut StoreOpaque,
4494 caller_instance: RuntimeComponentInstanceIndex,
4495 async_: bool,
4496 task_id: u32,
4497 ) -> Result<u32> {
4498 let (rep, is_host) = store
4499 .instance_state(self.runtime_instance(caller_instance))
4500 .handle_table()
4501 .subtask_rep(task_id)?;
4502 let waitable = if is_host {
4503 Waitable::Host(TableId::<HostTask>::new(rep))
4504 } else {
4505 Waitable::Guest(TableId::<GuestTask>::new(rep))
4506 };
4507 let concurrent_state = store.concurrent_state_mut()?;
4508
4509 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4510
4511 waitable.trap_if_in_waitable_set(concurrent_state)?;
4512
4513 let needs_block;
4514 if let Waitable::Host(host_task) = waitable {
4515 let state = &mut concurrent_state.get_mut(host_task)?.state;
4516 match state {
4517 HostTaskState::CalleeRunning(handle) => {
4524 handle.abort();
4525 *state = HostTaskState::CalleeCancelling;
4526 needs_block = true;
4527 }
4528
4529 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4532 bail!(Trap::SubtaskCancelAfterTerminal);
4533 }
4534 HostTaskState::CalleeDone { cancelled: false } => {
4535 *state = HostTaskState::CalleeDone { cancelled: true };
4538 needs_block = false;
4539 }
4540
4541 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4544 bail_bug!("invalid states for host callee")
4545 }
4546 }
4547 } else {
4548 let guest_task = TableId::<GuestTask>::new(rep);
4549 let task = concurrent_state.get_mut(guest_task)?;
4550
4551 if task.cancel_requested {
4554 bail!(Trap::SubtaskCancelAfterTerminal);
4555 }
4556
4557 if !task.already_lowered_parameters() {
4558 store.cancel_guest_subtask_without_lowered_parameters(
4559 self.runtime_instance(caller_instance),
4560 guest_task,
4561 )?;
4562 return Ok(Status::StartCancelled as u32);
4563 } else if !task.returned_or_cancelled() {
4564 task.cancel_requested = true;
4571 task.event = Some(Event::Cancelled);
4572 let runtime_instance = task.instance;
4573 for thread in task.threads.clone() {
4574 let thread = QualifiedThreadId {
4575 task: guest_task,
4576 thread,
4577 };
4578 let concurrent_state = store.concurrent_state_mut()?;
4579 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4580
4581 let yield_ = |store: &mut StoreOpaque| {
4582 let state = store.instance_state(runtime_instance).concurrent_state();
4587 let old_do_not_suspend = state.do_not_suspend;
4588 state.do_not_suspend = false;
4589
4590 let caller = store.current_guest_thread()?;
4591
4592 let state = store.concurrent_state_mut()?;
4597 let set = state.get_mut(caller.thread)?.sync_call_set;
4598 waitable.join(state, Some(set))?;
4599
4600 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4601
4602 let state = store.concurrent_state_mut()?;
4603 waitable.join(state, None)?;
4604
4605 store
4606 .instance_state(runtime_instance)
4607 .concurrent_state()
4608 .do_not_suspend = old_do_not_suspend;
4609
4610 Ok::<(), crate::Error>(())
4611 };
4612
4613 match thread_mut.wake_on_cancel.take() {
4614 WakeOnCancel::Waiting(set) => {
4615 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4617 let item = WorkItem::GuestCall {
4618 instance: runtime_instance,
4619 call: GuestCall {
4620 thread,
4621 kind: GuestCallKind::DeliverEvent {
4622 instance,
4623 set: Some(set),
4624 },
4625 },
4626 };
4627 concurrent_state.set_switch_item(item)?;
4628
4629 yield_(store)?;
4630
4631 break;
4632 }
4633 WakeOnCancel::Yielding => {
4634 if concurrent_state.promote_thread_work_item(thread)? {
4635 yield_(store)?;
4636 break;
4637 } else if store
4638 .instance_state(runtime_instance)
4639 .concurrent_state()
4640 .pending
4641 .contains_key(&thread)
4642 {
4643 store
4653 .concurrent_state_mut()?
4654 .get_mut(thread.thread)?
4655 .wake_on_cancel = WakeOnCancel::Yielding;
4656 } else {
4657 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4658 }
4659 }
4660 WakeOnCancel::None => {}
4661 }
4662 }
4663
4664 needs_block = !store
4667 .concurrent_state_mut()?
4668 .get_mut(guest_task)?
4669 .returned_or_cancelled()
4670 } else {
4671 needs_block = false;
4672 }
4673 };
4674
4675 if needs_block {
4679 if async_ {
4680 return Ok(BLOCKED);
4681 }
4682
4683 let old_next_switch_item = if is_host {
4693 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4694 } else {
4695 None
4696 };
4697
4698 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4701
4702 if let Some(item) = old_next_switch_item {
4703 let state = store.concurrent_state_mut()?;
4704 state.restore_next_switch_item(item)?;
4705 }
4706
4707 }
4709
4710 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4711 if let Some(Event::Subtask {
4712 status: status @ (Status::Returned | Status::ReturnCancelled),
4713 }) = event
4714 {
4715 Ok(status as u32)
4716 } else {
4717 bail!(Trap::SubtaskCancelAfterTerminal);
4718 }
4719 }
4720}
4721
4722pub trait VMComponentAsyncStore {
4730 unsafe fn prepare_call(
4736 &mut self,
4737 instance: Instance,
4738 memory: *mut VMMemoryDefinition,
4739 start: NonNull<VMFuncRef>,
4740 return_: NonNull<VMFuncRef>,
4741 caller_instance: RuntimeComponentInstanceIndex,
4742 callee_instance: RuntimeComponentInstanceIndex,
4743 task_return_type: TypeTupleIndex,
4744 callee_async: bool,
4745 string_encoding: StringEncoding,
4746 result_count: u32,
4747 storage: *mut ValRaw,
4748 storage_len: usize,
4749 ) -> Result<()>;
4750
4751 unsafe fn start_call(
4754 &mut self,
4755 instance: Instance,
4756 callback: *mut VMFuncRef,
4757 post_return: *mut VMFuncRef,
4758 callee: NonNull<VMFuncRef>,
4759 param_count: u32,
4760 result_count: u32,
4761 flags: u32,
4762 storage: *mut MaybeUninit<ValRaw>,
4763 storage_len: usize,
4764 ) -> Result<()>;
4765
4766 fn future_write(
4768 &mut self,
4769 instance: Instance,
4770 caller: RuntimeComponentInstanceIndex,
4771 ty: TypeFutureTableIndex,
4772 options: OptionsIndex,
4773 future: u32,
4774 address: u32,
4775 ) -> Result<u32>;
4776
4777 fn future_read(
4779 &mut self,
4780 instance: Instance,
4781 caller: RuntimeComponentInstanceIndex,
4782 ty: TypeFutureTableIndex,
4783 options: OptionsIndex,
4784 future: u32,
4785 address: u32,
4786 ) -> Result<u32>;
4787
4788 fn future_drop_writable(
4790 &mut self,
4791 instance: Instance,
4792 ty: TypeFutureTableIndex,
4793 writer: u32,
4794 ) -> Result<()>;
4795
4796 fn stream_write(
4798 &mut self,
4799 instance: Instance,
4800 caller: RuntimeComponentInstanceIndex,
4801 ty: TypeStreamTableIndex,
4802 options: OptionsIndex,
4803 stream: u32,
4804 address: u32,
4805 count: u32,
4806 ) -> Result<u32>;
4807
4808 fn stream_read(
4810 &mut self,
4811 instance: Instance,
4812 caller: RuntimeComponentInstanceIndex,
4813 ty: TypeStreamTableIndex,
4814 options: OptionsIndex,
4815 stream: u32,
4816 address: u32,
4817 count: u32,
4818 ) -> Result<u32>;
4819
4820 fn flat_stream_write(
4823 &mut self,
4824 instance: Instance,
4825 caller: RuntimeComponentInstanceIndex,
4826 ty: TypeStreamTableIndex,
4827 options: OptionsIndex,
4828 payload_size: u32,
4829 payload_align: u32,
4830 stream: u32,
4831 address: u32,
4832 count: u32,
4833 ) -> Result<u32>;
4834
4835 fn flat_stream_read(
4838 &mut self,
4839 instance: Instance,
4840 caller: RuntimeComponentInstanceIndex,
4841 ty: TypeStreamTableIndex,
4842 options: OptionsIndex,
4843 payload_size: u32,
4844 payload_align: u32,
4845 stream: u32,
4846 address: u32,
4847 count: u32,
4848 ) -> Result<u32>;
4849
4850 fn stream_drop_writable(
4852 &mut self,
4853 instance: Instance,
4854 ty: TypeStreamTableIndex,
4855 writer: u32,
4856 ) -> Result<()>;
4857
4858 fn error_context_debug_message(
4860 &mut self,
4861 instance: Instance,
4862 ty: TypeComponentLocalErrorContextTableIndex,
4863 options: OptionsIndex,
4864 err_ctx_handle: u32,
4865 debug_msg_address: u32,
4866 ) -> Result<()>;
4867
4868 fn thread_new_indirect(
4870 &mut self,
4871 instance: Instance,
4872 caller: RuntimeComponentInstanceIndex,
4873 func_ty_idx: ModuleInternedTypeIndex,
4874 start_func_table_idx: RuntimeTableIndex,
4875 start_func_idx: u32,
4876 context: i32,
4877 ) -> Result<u32>;
4878}
4879
4880impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4882 unsafe fn prepare_call(
4883 &mut self,
4884 instance: Instance,
4885 memory: *mut VMMemoryDefinition,
4886 start: NonNull<VMFuncRef>,
4887 return_: NonNull<VMFuncRef>,
4888 caller_instance: RuntimeComponentInstanceIndex,
4889 callee_instance: RuntimeComponentInstanceIndex,
4890 task_return_type: TypeTupleIndex,
4891 callee_async: bool,
4892 string_encoding: StringEncoding,
4893 result_count_or_max_if_async: u32,
4894 storage: *mut ValRaw,
4895 storage_len: usize,
4896 ) -> Result<()> {
4897 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4901
4902 unsafe {
4903 instance.prepare_call(
4904 StoreContextMut(self),
4905 start,
4906 return_,
4907 caller_instance,
4908 callee_instance,
4909 task_return_type,
4910 callee_async,
4911 memory,
4912 string_encoding,
4913 match result_count_or_max_if_async {
4914 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4915 params,
4916 has_result: false,
4917 },
4918 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4919 params,
4920 has_result: true,
4921 },
4922 result_count => CallerInfo::Sync {
4923 params,
4924 result_count,
4925 },
4926 },
4927 )
4928 }
4929 }
4930
4931 unsafe fn start_call(
4932 &mut self,
4933 instance: Instance,
4934 callback: *mut VMFuncRef,
4935 post_return: *mut VMFuncRef,
4936 callee: NonNull<VMFuncRef>,
4937 param_count: u32,
4938 result_count: u32,
4939 flags: u32,
4940 storage: *mut MaybeUninit<ValRaw>,
4941 storage_len: usize,
4942 ) -> Result<()> {
4943 unsafe {
4944 instance.start_call(
4945 StoreContextMut(self),
4946 callback,
4947 post_return,
4948 callee,
4949 param_count,
4950 result_count,
4951 flags,
4952 core::slice::from_raw_parts_mut(storage, storage_len),
4956 )
4957 }
4958 }
4959
4960 fn future_write(
4961 &mut self,
4962 instance: Instance,
4963 caller: RuntimeComponentInstanceIndex,
4964 ty: TypeFutureTableIndex,
4965 options: OptionsIndex,
4966 future: u32,
4967 address: u32,
4968 ) -> Result<u32> {
4969 instance
4970 .guest_write(
4971 StoreContextMut(self),
4972 caller,
4973 TransmitIndex::Future(ty),
4974 options,
4975 None,
4976 future,
4977 address,
4978 1,
4979 )
4980 .map(|result| result.encode())
4981 }
4982
4983 fn future_read(
4984 &mut self,
4985 instance: Instance,
4986 caller: RuntimeComponentInstanceIndex,
4987 ty: TypeFutureTableIndex,
4988 options: OptionsIndex,
4989 future: u32,
4990 address: u32,
4991 ) -> Result<u32> {
4992 instance
4993 .guest_read(
4994 StoreContextMut(self),
4995 caller,
4996 TransmitIndex::Future(ty),
4997 options,
4998 None,
4999 future,
5000 address,
5001 1,
5002 )
5003 .map(|result| result.encode())
5004 }
5005
5006 fn stream_write(
5007 &mut self,
5008 instance: Instance,
5009 caller: RuntimeComponentInstanceIndex,
5010 ty: TypeStreamTableIndex,
5011 options: OptionsIndex,
5012 stream: u32,
5013 address: u32,
5014 count: u32,
5015 ) -> Result<u32> {
5016 instance
5017 .guest_write(
5018 StoreContextMut(self),
5019 caller,
5020 TransmitIndex::Stream(ty),
5021 options,
5022 None,
5023 stream,
5024 address,
5025 count,
5026 )
5027 .map(|result| result.encode())
5028 }
5029
5030 fn stream_read(
5031 &mut self,
5032 instance: Instance,
5033 caller: RuntimeComponentInstanceIndex,
5034 ty: TypeStreamTableIndex,
5035 options: OptionsIndex,
5036 stream: u32,
5037 address: u32,
5038 count: u32,
5039 ) -> Result<u32> {
5040 instance
5041 .guest_read(
5042 StoreContextMut(self),
5043 caller,
5044 TransmitIndex::Stream(ty),
5045 options,
5046 None,
5047 stream,
5048 address,
5049 count,
5050 )
5051 .map(|result| result.encode())
5052 }
5053
5054 fn future_drop_writable(
5055 &mut self,
5056 instance: Instance,
5057 ty: TypeFutureTableIndex,
5058 writer: u32,
5059 ) -> Result<()> {
5060 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5061 }
5062
5063 fn flat_stream_write(
5064 &mut self,
5065 instance: Instance,
5066 caller: RuntimeComponentInstanceIndex,
5067 ty: TypeStreamTableIndex,
5068 options: OptionsIndex,
5069 payload_size: u32,
5070 payload_align: u32,
5071 stream: u32,
5072 address: u32,
5073 count: u32,
5074 ) -> Result<u32> {
5075 instance
5076 .guest_write(
5077 StoreContextMut(self),
5078 caller,
5079 TransmitIndex::Stream(ty),
5080 options,
5081 Some(FlatAbi {
5082 size: payload_size,
5083 align: payload_align,
5084 }),
5085 stream,
5086 address,
5087 count,
5088 )
5089 .map(|result| result.encode())
5090 }
5091
5092 fn flat_stream_read(
5093 &mut self,
5094 instance: Instance,
5095 caller: RuntimeComponentInstanceIndex,
5096 ty: TypeStreamTableIndex,
5097 options: OptionsIndex,
5098 payload_size: u32,
5099 payload_align: u32,
5100 stream: u32,
5101 address: u32,
5102 count: u32,
5103 ) -> Result<u32> {
5104 instance
5105 .guest_read(
5106 StoreContextMut(self),
5107 caller,
5108 TransmitIndex::Stream(ty),
5109 options,
5110 Some(FlatAbi {
5111 size: payload_size,
5112 align: payload_align,
5113 }),
5114 stream,
5115 address,
5116 count,
5117 )
5118 .map(|result| result.encode())
5119 }
5120
5121 fn stream_drop_writable(
5122 &mut self,
5123 instance: Instance,
5124 ty: TypeStreamTableIndex,
5125 writer: u32,
5126 ) -> Result<()> {
5127 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5128 }
5129
5130 fn error_context_debug_message(
5131 &mut self,
5132 instance: Instance,
5133 ty: TypeComponentLocalErrorContextTableIndex,
5134 options: OptionsIndex,
5135 err_ctx_handle: u32,
5136 debug_msg_address: u32,
5137 ) -> Result<()> {
5138 instance.error_context_debug_message(
5139 StoreContextMut(self),
5140 ty,
5141 options,
5142 err_ctx_handle,
5143 debug_msg_address,
5144 )
5145 }
5146
5147 fn thread_new_indirect(
5148 &mut self,
5149 instance: Instance,
5150 caller: RuntimeComponentInstanceIndex,
5151 func_ty_idx: ModuleInternedTypeIndex,
5152 start_func_table_idx: RuntimeTableIndex,
5153 start_func_idx: u32,
5154 context: i32,
5155 ) -> Result<u32> {
5156 instance.thread_new_indirect(
5157 StoreContextMut(self),
5158 caller,
5159 func_ty_idx,
5160 start_func_table_idx,
5161 start_func_idx,
5162 context,
5163 )
5164 }
5165}
5166
5167type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5168
5169async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5172where
5173 F: Future,
5174{
5175 let mut future = pin!(future);
5176 future::poll_fn(|cx| {
5177 let old_thread = match tls::get(|store| store.set_thread(task)) {
5178 Ok(thread) => thread,
5179 Err(error) => return Poll::Ready(Err(error)),
5180 };
5181 let result = future.as_mut().poll(cx);
5182 match tls::get(|store| store.set_thread(old_thread)) {
5183 Ok(_) => result.map(Ok),
5184 Err(error) => Poll::Ready(Err(error)),
5185 }
5186 })
5187 .await
5188}
5189
5190pub(crate) struct HostTask {
5194 common: WaitableCommon,
5195
5196 call_context: CallContext,
5199
5200 state: HostTaskState,
5201
5202 group: TaskGroupId,
5203}
5204
5205enum HostTaskState {
5206 CalleeStarted,
5211
5212 CalleeRunning(JoinHandle),
5217
5218 CalleeCancelling,
5222
5223 CalleeFinished(LiftedResult),
5227
5228 CalleeDone { cancelled: bool },
5231}
5232
5233impl HostTask {
5234 fn new(
5235 concurrent_state: &mut ConcurrentState,
5236 state: HostTaskState,
5237 caller: QualifiedThreadId,
5238 ) -> Result<Self> {
5239 let group = concurrent_state.get_mut(caller.task)?.group;
5240 concurrent_state.increment_group_ref_count(group)?;
5241
5242 Ok(Self {
5243 common: WaitableCommon::default(),
5244 call_context: CallContext::default(),
5245 state,
5246 group,
5247 })
5248 }
5249}
5250
5251impl TableDebug for HostTask {
5252 fn type_name() -> &'static str {
5253 "HostTask"
5254 }
5255}
5256
5257type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5258
5259enum Caller {
5261 Host {
5263 tx: Option<oneshot::Sender<LiftedResult>>,
5265 caller: Option<TableId<HostTask>>,
5269 },
5270 Guest {
5272 thread: QualifiedThreadId,
5274 },
5275}
5276
5277struct LiftResult {
5280 lift: RawLift,
5281 ty: TypeTupleIndex,
5282 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5283 string_encoding: StringEncoding,
5284}
5285
5286#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5291pub(crate) struct QualifiedThreadId {
5292 task: TableId<GuestTask>,
5293 thread: TableId<GuestThread>,
5294}
5295
5296impl QualifiedThreadId {
5297 fn qualify(
5298 state: &mut ConcurrentState,
5299 thread: TableId<GuestThread>,
5300 ) -> Result<QualifiedThreadId> {
5301 Ok(QualifiedThreadId {
5302 task: state.get_mut(thread)?.parent_task,
5303 thread,
5304 })
5305 }
5306}
5307
5308impl fmt::Debug for QualifiedThreadId {
5309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5310 f.debug_tuple("QualifiedThreadId")
5311 .field(&self.task.rep())
5312 .field(&self.thread.rep())
5313 .finish()
5314 }
5315}
5316
5317enum GuestThreadState {
5318 NotStartedImplicit,
5319 NotStartedExplicit(
5320 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5321 ),
5322 Running,
5323 Suspended(StoreFiber<'static>),
5324 Ready {
5325 fiber: StoreFiber<'static>,
5326 },
5327 Completed,
5328}
5329
5330impl GuestThreadState {
5331 fn check_no_fiber(&self) -> Result<()> {
5332 match self {
5333 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5334 Self::NotStartedImplicit
5335 | Self::NotStartedExplicit(_)
5336 | Self::Running
5337 | Self::Completed => Ok(()),
5338 }
5339 }
5340}
5341
5342impl fmt::Debug for GuestThreadState {
5343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5344 match self {
5345 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5346 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5347 Self::Running => f.debug_tuple("Running").finish(),
5348 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5349 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5350 Self::Completed => f.debug_tuple("Completed").finish(),
5351 }
5352 }
5353}
5354
5355#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5356enum WakeOnCancel {
5357 None,
5358 Waiting(TableId<WaitableSet>),
5359 Yielding,
5360}
5361
5362impl WakeOnCancel {
5363 fn is_none(self) -> bool {
5364 matches!(self, WakeOnCancel::None)
5365 }
5366
5367 fn replace(&mut self, other: WakeOnCancel) -> Self {
5368 let old = *self;
5369 *self = other;
5370 old
5371 }
5372
5373 fn take(&mut self) -> Self {
5374 self.replace(WakeOnCancel::None)
5375 }
5376}
5377
5378pub struct GuestThread {
5379 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5382 parent_task: TableId<GuestTask>,
5384 wake_on_cancel: WakeOnCancel,
5387 state: GuestThreadState,
5389 instance_rep: Option<u32>,
5392 sync_call_set: TableId<WaitableSet>,
5394 old_do_not_suspend: Option<bool>,
5397}
5398
5399impl GuestThread {
5400 fn from_instance(
5403 state: Pin<&mut ComponentInstance>,
5404 caller_instance: RuntimeComponentInstanceIndex,
5405 guest_thread: u32,
5406 ) -> Result<TableId<Self>> {
5407 let rep = state.instance_states().0[caller_instance]
5408 .thread_handle_table()
5409 .guest_thread_rep(guest_thread)?;
5410 Ok(TableId::new(rep))
5411 }
5412
5413 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5414 let sync_call_set = state.push(WaitableSet {
5415 is_sync_call_set: true,
5416 ..WaitableSet::default()
5417 })?;
5418 Ok(Self {
5419 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5420 parent_task,
5421 wake_on_cancel: WakeOnCancel::None,
5422 state: GuestThreadState::NotStartedImplicit,
5423 instance_rep: None,
5424 sync_call_set,
5425 old_do_not_suspend: None,
5426 })
5427 }
5428
5429 fn new_explicit(
5430 state: &mut ConcurrentState,
5431 parent_task: TableId<GuestTask>,
5432 start_func: Box<
5433 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5434 >,
5435 ) -> Result<Self> {
5436 state.increment_task_ref_count(parent_task)?;
5437 let sync_call_set = state.push(WaitableSet {
5438 is_sync_call_set: true,
5439 ..WaitableSet::default()
5440 })?;
5441 Ok(Self {
5442 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5443 parent_task,
5444 wake_on_cancel: WakeOnCancel::None,
5445 state: GuestThreadState::NotStartedExplicit(start_func),
5446 instance_rep: None,
5447 sync_call_set,
5448 old_do_not_suspend: None,
5449 })
5450 }
5451}
5452
5453impl TableDebug for GuestThread {
5454 fn type_name() -> &'static str {
5455 "GuestThread"
5456 }
5457}
5458
5459enum SyncResult {
5460 NotProduced,
5461 Produced(Option<ValRaw>),
5462 Taken,
5463}
5464
5465impl SyncResult {
5466 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5467 Ok(match mem::replace(self, SyncResult::Taken) {
5468 SyncResult::NotProduced => None,
5469 SyncResult::Produced(val) => Some(val),
5470 SyncResult::Taken => {
5471 bail_bug!("attempted to take a synchronous result that was already taken")
5472 }
5473 })
5474 }
5475}
5476
5477pub(crate) struct GuestTask {
5479 common: WaitableCommon,
5481 lower_params: Option<RawLower>,
5483 lift_result: Option<LiftResult>,
5485 result: Option<LiftedResult>,
5488 callback: Option<CallbackFn>,
5491 caller: Caller,
5493 call_context: CallContext,
5498 sync_result: SyncResult,
5501 cancel_request_delivered: bool,
5505 cancel_requested: bool,
5510 starting_sent: bool,
5513 instance: RuntimeInstance,
5520 event: Option<Event>,
5522 exited: bool,
5524 threads: HashSet<TableId<GuestThread>>,
5526 async_typed: bool,
5529 async_lifted: bool,
5532 decremented_interesting_task_count: bool,
5535 group: TaskGroupId,
5537 ref_count: usize,
5542}
5543
5544impl GuestTask {
5545 fn already_lowered_parameters(&self) -> bool {
5546 self.lower_params.is_none()
5548 }
5549
5550 fn returned_or_cancelled(&self) -> bool {
5551 self.lift_result.is_none()
5553 }
5554
5555 fn new(
5556 state: &mut ConcurrentState,
5557 lower_params: RawLower,
5558 lift_result: LiftResult,
5559 caller: Caller,
5560 callback: Option<CallbackFn>,
5561 instance: RuntimeInstance,
5562 async_typed: bool,
5563 async_lifted: bool,
5564 ) -> Result<QualifiedThreadId> {
5565 let group = match caller {
5566 Caller::Guest { thread } => {
5567 let group = state.get_mut(thread.task)?.group;
5568 state.increment_group_ref_count(group)?;
5569 group
5570 }
5571 Caller::Host { .. } => state.make_task_group()?,
5572 };
5573
5574 let task = state.push(Self {
5575 common: WaitableCommon::default(),
5576 lower_params: Some(lower_params),
5577 lift_result: Some(lift_result),
5578 result: None,
5579 callback,
5580 caller,
5581 call_context: CallContext::default(),
5582 sync_result: SyncResult::NotProduced,
5583 cancel_request_delivered: false,
5584 cancel_requested: false,
5585 starting_sent: false,
5586 instance,
5587 event: None,
5588 exited: false,
5589 threads: HashSet::new(),
5590 async_typed,
5591 async_lifted,
5592 decremented_interesting_task_count: false,
5593 group,
5594 ref_count: 2,
5597 })?;
5598 let new_thread = GuestThread::new_implicit(state, task)?;
5599 let thread = state.push(new_thread)?;
5600 state.get_mut(task)?.threads.insert(thread);
5601 state.interesting_tasks += 1;
5602 let thread = QualifiedThreadId { task, thread };
5603 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5604 Ok(thread)
5605 }
5606}
5607
5608impl TableDebug for GuestTask {
5609 fn type_name() -> &'static str {
5610 "GuestTask"
5611 }
5612}
5613
5614#[derive(Default)]
5616struct WaitableCommon {
5617 event: Option<Event>,
5619 set: Option<TableId<WaitableSet>>,
5621 handle: Option<u32>,
5623}
5624
5625#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5627enum Waitable {
5628 Host(TableId<HostTask>),
5630 Guest(TableId<GuestTask>),
5632 Transmit(TableId<TransmitHandle>),
5634}
5635
5636impl Waitable {
5637 fn from_instance(
5640 state: Pin<&mut ComponentInstance>,
5641 caller_instance: RuntimeComponentInstanceIndex,
5642 waitable: u32,
5643 ) -> Result<Self> {
5644 use crate::runtime::vm::component::Waitable;
5645
5646 let (waitable, kind) = state.instance_states().0[caller_instance]
5647 .handle_table()
5648 .waitable_rep(waitable)?;
5649
5650 Ok(match kind {
5651 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5652 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5653 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5654 })
5655 }
5656
5657 fn rep(&self) -> u32 {
5659 match self {
5660 Self::Host(id) => id.rep(),
5661 Self::Guest(id) => id.rep(),
5662 Self::Transmit(id) => id.rep(),
5663 }
5664 }
5665
5666 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5670 log::trace!("waitable {self:?} join set {set:?}");
5671
5672 let old = mem::replace(&mut self.common(state)?.set, set);
5673
5674 if let Some(old) = old {
5675 match *self {
5676 Waitable::Host(id) => state.remove_child(id, old),
5677 Waitable::Guest(id) => state.remove_child(id, old),
5678 Waitable::Transmit(id) => state.remove_child(id, old),
5679 }?;
5680
5681 state.get_mut(old)?.ready.remove(self);
5682 }
5683
5684 if let Some(set) = set {
5685 match *self {
5686 Waitable::Host(id) => state.add_child(id, set),
5687 Waitable::Guest(id) => state.add_child(id, set),
5688 Waitable::Transmit(id) => state.add_child(id, set),
5689 }?;
5690
5691 if self.common(state)?.event.is_some() {
5692 self.mark_ready(state)?;
5693 }
5694 }
5695
5696 Ok(())
5697 }
5698
5699 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5701 Ok(match self {
5702 Self::Host(id) => &mut state.get_mut(*id)?.common,
5703 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5704 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5705 })
5706 }
5707
5708 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5714 if self.common(state)?.set.is_some() {
5715 bail!(Trap::WaitableSyncAndAsync);
5716 }
5717 Ok(())
5718 }
5719
5720 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5726 if let Some(set) = self.common(state)?.set
5727 && state.get_mut(set)?.is_sync_call_set
5728 {
5729 bail!(Trap::WaitableSyncAndAsync);
5730 }
5731 Ok(())
5732 }
5733
5734 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5738 log::trace!("set event for {self:?}: {event:?}");
5739 self.common(state)?.event = event;
5740 self.mark_ready(state)
5741 }
5742
5743 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5745 let common = self.common(state)?;
5746 let event = common.event.take();
5747 if let Some(set) = self.common(state)?.set {
5748 state.get_mut(set)?.ready.remove(self);
5749 }
5750
5751 Ok(event)
5752 }
5753
5754 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5758 if let Some(set) = self.common(state)?.set {
5759 state.get_mut(set)?.ready.insert(*self);
5760 state.wake_waiter(set)?;
5761 }
5762 Ok(())
5763 }
5764
5765 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5768 match self {
5769 Self::Host(task) => state.delete_host_task(*task),
5770 Self::Guest(task) => state.decrement_task_ref_count(*task),
5771 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5772 }
5773 }
5774}
5775
5776impl fmt::Debug for Waitable {
5777 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5778 match self {
5779 Self::Host(id) => write!(f, "{id:?}"),
5780 Self::Guest(id) => write!(f, "{id:?}"),
5781 Self::Transmit(id) => write!(f, "{id:?}"),
5782 }
5783 }
5784}
5785
5786#[derive(Default)]
5788struct WaitableSet {
5789 ready: BTreeSet<Waitable>,
5791 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5793 num_waiting: usize,
5796 is_sync_call_set: bool,
5799}
5800
5801impl WaitableSet {
5802 fn stop_waiting(&mut self) -> Result<()> {
5804 self.num_waiting = match self.num_waiting.checked_sub(1) {
5805 Some(n) => n,
5806 None => bail_bug!("waiter not accounted for in waitable set"),
5807 };
5808 Ok(())
5809 }
5810}
5811
5812impl TableDebug for WaitableSet {
5813 fn type_name() -> &'static str {
5814 "WaitableSet"
5815 }
5816}
5817
5818type RawLower =
5820 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5821
5822type RawLift = Box<
5824 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5825>;
5826
5827type LiftedResult = Box<dyn Any + Send + Sync>;
5831
5832struct DummyResult;
5835
5836#[derive(Default)]
5838pub struct ConcurrentInstanceState {
5839 backpressure: u16,
5841 do_not_enter: bool,
5843 do_not_suspend: bool,
5846 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5849}
5850
5851impl ConcurrentInstanceState {
5852 pub fn pending_is_empty(&self) -> bool {
5853 self.pending.is_empty()
5854 }
5855}
5856
5857#[derive(Debug, Copy, Clone)]
5858pub(crate) enum CurrentThread {
5859 Guest(QualifiedThreadId),
5862 Host(TableId<HostTask>),
5864 DeferredHost(QualifiedThreadId),
5867 None,
5870}
5871
5872impl CurrentThread {
5873 fn guest(&self) -> Option<&QualifiedThreadId> {
5874 match self {
5875 Self::Guest(id) => Some(id),
5876 _ => None,
5877 }
5878 }
5879
5880 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5881 match self {
5882 Self::Guest(id) => Some(id.task),
5883 _ => None,
5884 }
5885 }
5886
5887 fn is_none(&self) -> bool {
5888 matches!(self, Self::None)
5889 }
5890}
5891
5892impl From<QualifiedThreadId> for CurrentThread {
5893 fn from(id: QualifiedThreadId) -> Self {
5894 Self::Guest(id)
5895 }
5896}
5897
5898impl From<TableId<HostTask>> for CurrentThread {
5899 fn from(id: TableId<HostTask>) -> Self {
5900 Self::Host(id)
5901 }
5902}
5903
5904enum Priority {
5905 Switch,
5906 High,
5907 Low,
5908}
5909
5910pub struct ConcurrentState {
5912 unforced_current_thread: CurrentThread,
5918
5919 deferred_host_call_context: Option<CallContext>,
5925
5926 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5931 table: AlwaysMut<ResourceTable>,
5933 switch_item: Option<WorkItem>,
5941 next_switch_item: Option<WorkItem>,
5947 high_priority: VecDeque<WorkItem>,
5949 low_priority: VecDeque<WorkItem>,
5951 suspend_reason: Option<SuspendReason>,
5955 worker: Option<StoreFiber<'static>>,
5959 worker_item: Option<WorkerItem>,
5961
5962 global_error_context_ref_counts:
5975 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5976
5977 interesting_tasks: usize,
5990
5991 interesting_tasks_empty_waker: Option<Waker>,
5995
5996 ready_for_concurrent_call_waker: Option<Waker>,
6001
6002 event_loop_running: bool,
6004
6005 saved_next_switch_items: Vec<Option<WorkItem>>,
6008
6009 #[cfg(feature = "task-group-hook")]
6011 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6012}
6013
6014impl Default for ConcurrentState {
6015 fn default() -> Self {
6016 Self {
6017 unforced_current_thread: CurrentThread::None,
6018 deferred_host_call_context: None,
6019 table: AlwaysMut::new(ResourceTable::new()),
6020 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6021 switch_item: None,
6022 next_switch_item: None,
6023 high_priority: VecDeque::new(),
6024 low_priority: VecDeque::new(),
6025 suspend_reason: None,
6026 worker: None,
6027 worker_item: None,
6028 global_error_context_ref_counts: BTreeMap::new(),
6029 interesting_tasks: 0,
6030 interesting_tasks_empty_waker: None,
6031 ready_for_concurrent_call_waker: None,
6032 event_loop_running: false,
6033 saved_next_switch_items: Vec::new(),
6034 #[cfg(feature = "task-group-hook")]
6035 task_group_hook: None,
6036 }
6037 }
6038}
6039
6040impl ConcurrentState {
6041 pub(crate) fn take_fibers_and_futures(
6058 &mut self,
6059 fibers: &mut Vec<StoreFiber<'static>>,
6060 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6061 ) {
6062 let ConcurrentState {
6063 table,
6064 worker,
6065 switch_item,
6066 next_switch_item,
6067 high_priority,
6068 low_priority,
6069 saved_next_switch_items,
6070 futures: pending_futures,
6071 worker_item: _,
6072 unforced_current_thread: _,
6073 deferred_host_call_context: _,
6074 suspend_reason: _,
6075 global_error_context_ref_counts: _,
6076 interesting_tasks: _,
6077 interesting_tasks_empty_waker: _,
6078 ready_for_concurrent_call_waker: _,
6079 event_loop_running: _,
6080 #[cfg(feature = "task-group-hook")]
6081 task_group_hook: _,
6082 } = self;
6083
6084 let mut items = Vec::new();
6085 for (_, entry) in table.get_mut().iter_mut() {
6086 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6087 for mode in mem::take(&mut set.waiting).into_values() {
6088 match mode {
6089 WaitMode::Fiber(fiber) => {
6090 fibers.push(fiber);
6091 }
6092 WaitMode::Callback(_) => {}
6093 }
6094 }
6095 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6096 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6097 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6098 fibers.push(fiber);
6099 }
6100 GuestThreadState::NotStartedImplicit
6101 | GuestThreadState::NotStartedExplicit(_)
6102 | GuestThreadState::Running
6103 | GuestThreadState::Completed => {}
6104 }
6105 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6106 if let Some(item) = item.take() {
6107 items.push(item);
6108 }
6109 }
6110 }
6111
6112 if let Some(fiber) = worker.take() {
6113 fibers.push(fiber);
6114 }
6115
6116 let mut handle_item = |item| match item {
6117 WorkItem::ResumeFiber { fiber, .. } => {
6118 fibers.push(fiber);
6119 }
6120 WorkItem::PushFuture(future) => {
6121 pending_futures
6122 .get_mut()
6123 .as_mut()
6124 .unwrap()
6125 .push(future.into_inner());
6126 }
6127 WorkItem::ResumeThread { .. }
6128 | WorkItem::GuestCall { .. }
6129 | WorkItem::WorkerFunction(_) => {}
6130 };
6131
6132 for item in items {
6133 handle_item(item);
6134 }
6135 if let Some(item) = switch_item.take() {
6136 handle_item(item);
6137 }
6138 if let Some(item) = next_switch_item.take() {
6139 handle_item(item);
6140 }
6141 for item in mem::take(high_priority) {
6142 handle_item(item);
6143 }
6144 for item in mem::take(low_priority) {
6145 handle_item(item);
6146 }
6147 for item in mem::take(saved_next_switch_items)
6148 .into_iter()
6149 .filter_map(|v| v)
6150 {
6151 handle_item(item);
6152 }
6153
6154 if let Some(them) = pending_futures.get_mut().take() {
6155 futures.push(them);
6156 }
6157 }
6158
6159 #[cfg(feature = "gc")]
6160 pub(crate) fn trace_fiber_roots(
6161 &mut self,
6162 modules: &ModuleRegistry,
6163 unwind: &dyn Unwind,
6164 gc_roots_list: &mut GcRootsList,
6165 ) {
6166 let ConcurrentState {
6167 table,
6168 worker,
6169 switch_item,
6170 next_switch_item,
6171 high_priority,
6172 low_priority,
6173 saved_next_switch_items,
6174
6175 futures: _,
6179
6180 worker_item: _,
6182 unforced_current_thread: _,
6183 deferred_host_call_context: _,
6184 suspend_reason: _,
6185 global_error_context_ref_counts: _,
6186 interesting_tasks: _,
6187 interesting_tasks_empty_waker: _,
6188 ready_for_concurrent_call_waker: _,
6189 event_loop_running: _,
6190 #[cfg(feature = "task-group-hook")]
6191 task_group_hook: _,
6192 } = self;
6193
6194 for (_, entry) in table.get_mut().iter_mut() {
6195 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6196 for mode in set.waiting.values_mut() {
6197 match mode {
6198 WaitMode::Fiber(fiber) => {
6199 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6200 }
6201 WaitMode::Callback(_) => {}
6202 }
6203 }
6204 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6205 match &mut thread.state {
6206 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6207 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6208 }
6209 GuestThreadState::NotStartedImplicit
6210 | GuestThreadState::NotStartedExplicit(_)
6211 | GuestThreadState::Running
6212 | GuestThreadState::Completed => {}
6213 }
6214 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6215 entry.downcast_mut::<Option<WorkItem>>()
6216 {
6217 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6218 }
6219 }
6220
6221 if let Some(fiber) = worker {
6222 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6223 }
6224
6225 let mut handle_item = |item: &mut WorkItem| match item {
6226 WorkItem::ResumeFiber { fiber, .. } => {
6227 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6228 }
6229 WorkItem::PushFuture(_future) => {
6230 }
6233 WorkItem::ResumeThread { .. }
6234 | WorkItem::GuestCall { .. }
6235 | WorkItem::WorkerFunction(_) => {}
6236 };
6237
6238 if let Some(item) = switch_item {
6239 handle_item(item);
6240 }
6241 if let Some(item) = next_switch_item {
6242 handle_item(item);
6243 }
6244 for item in high_priority {
6245 handle_item(item);
6246 }
6247 for item in low_priority {
6248 handle_item(item);
6249 }
6250 for item in saved_next_switch_items
6251 .iter_mut()
6252 .filter_map(|v| v.as_mut())
6253 {
6254 handle_item(item);
6255 }
6256 }
6257
6258 fn schedule_suspended_thread(
6259 &mut self,
6260 thread: QualifiedThreadId,
6261 instance: RuntimeInstance,
6262 priority: Priority,
6263 ) -> Result<()> {
6264 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6266 bail_bug!("switch item already set");
6267 }
6268 let slot = &mut self.get_mut(thread.thread)?.state;
6269 if !matches!(slot, GuestThreadState::Suspended(_)) {
6270 bail!(Trap::CannotResumeThread);
6271 }
6272 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6273 else {
6274 unreachable!()
6275 };
6276 let item = WorkItem::ResumeFiber {
6277 instance,
6278 thread,
6279 fiber,
6280 };
6281 match priority {
6282 Priority::Switch => self.switch_item = Some(item),
6283 Priority::High => self.push_high_priority(item),
6284 Priority::Low => self.push_low_priority(item),
6285 }
6286 Ok(())
6287 }
6288
6289 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6290 let slot = &mut self.get_mut(thread)?.state;
6291 slot.check_no_fiber()?;
6292 *slot = GuestThreadState::Running;
6293 Ok(())
6294 }
6295
6296 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6297 let saved = self.push(None::<WorkItem>)?;
6300 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6301 *slot = self.next_switch_item.take();
6302 Ok(saved)
6303 }
6304
6305 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6306 if self.next_switch_item.is_some() {
6307 bail_bug!("next switch item already set when restoring");
6308 }
6309 self.next_switch_item = self.delete(saved)?;
6310 Ok(())
6311 }
6312
6313 fn take_callback_waiter(
6314 &mut self,
6315 set: TableId<WaitableSet>,
6316 thread: QualifiedThreadId,
6317 ) -> Result<Instance> {
6318 let waiting = &mut self.get_mut(set)?.waiting;
6319 let instance = match waiting.get(&thread) {
6320 Some(WaitMode::Callback(instance)) => *instance,
6321 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6322 };
6323 waiting.remove(&thread);
6324 Ok(instance)
6325 }
6326
6327 fn push<V: Send + Sync + 'static>(
6328 &mut self,
6329 value: V,
6330 ) -> Result<TableId<V>, ResourceTableError> {
6331 self.table.get_mut().push(value).map(TableId::from)
6332 }
6333
6334 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6335 self.table.get_mut().get_mut(&Resource::from(id))
6336 }
6337
6338 pub fn add_child<T: 'static, U: 'static>(
6339 &mut self,
6340 child: TableId<T>,
6341 parent: TableId<U>,
6342 ) -> Result<(), ResourceTableError> {
6343 self.table
6344 .get_mut()
6345 .add_child(Resource::from(child), Resource::from(parent))
6346 }
6347
6348 pub fn remove_child<T: 'static, U: 'static>(
6349 &mut self,
6350 child: TableId<T>,
6351 parent: TableId<U>,
6352 ) -> Result<(), ResourceTableError> {
6353 self.table
6354 .get_mut()
6355 .remove_child(Resource::from(child), Resource::from(parent))
6356 }
6357
6358 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6359 self.table.get_mut().delete(Resource::from(id))
6360 }
6361
6362 fn push_future(&mut self, future: HostTaskFuture) {
6363 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6370 }
6371
6372 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6373 if self.switch_item.is_some() {
6374 self.push_high_priority(item);
6377 bail_bug!("switch item already set");
6378 }
6379
6380 self.switch_item = Some(item);
6381 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6382
6383 Ok(())
6384 }
6385
6386 fn take_next_switch_item(&mut self) -> Result<()> {
6387 if let Some(item) = self.next_switch_item.take() {
6388 self.set_switch_item(item)?;
6389 }
6390 Ok(())
6391 }
6392
6393 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6396 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6399 return Ok(());
6400 };
6401 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6402 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6403 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6404 }
6405 let instance = self.get_mut(thread.task)?.instance;
6406 self.get_mut(thread.thread)?.wake_on_cancel.take();
6407 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6408
6409 let item = match mode {
6410 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6411 instance,
6412 thread,
6413 fiber,
6414 },
6415 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6416 instance,
6417 call: GuestCall {
6418 thread,
6419 kind: GuestCallKind::DeliverEvent {
6420 instance: callback_instance,
6421 set: Some(set),
6422 },
6423 },
6424 },
6425 };
6426 self.push_high_priority(item);
6427 Ok(())
6428 }
6429
6430 fn push_high_priority(&mut self, item: WorkItem) {
6431 self.high_priority.push_front(item);
6432 log::trace!(
6433 "push high priority: {:?}",
6434 self.high_priority.front().unwrap()
6435 );
6436 }
6437
6438 fn push_low_priority(&mut self, item: WorkItem) {
6439 self.low_priority.push_front(item);
6440 log::trace!(
6441 "push low priority: {:?}",
6442 self.low_priority.front().unwrap()
6443 );
6444 }
6445
6446 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6447 match priority {
6448 Priority::Switch => self.set_switch_item(item)?,
6449 Priority::High => self.push_high_priority(item),
6450 Priority::Low => self.push_low_priority(item),
6451 }
6452
6453 Ok(())
6454 }
6455
6456 fn promote_instance_local_thread_work_item(
6457 &mut self,
6458 current_instance: RuntimeInstance,
6459 ) -> Result<bool> {
6460 log::trace!("promote thread work items for {current_instance:?}");
6461
6462 self.promote_work_item_matching(|item: &WorkItem| {
6463 let result = match item {
6464 WorkItem::ResumeThread { instance, .. }
6465 | WorkItem::ResumeFiber { instance, .. }
6466 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6467 _ => false,
6468 };
6469
6470 log::trace!("candidate {item:?}: {result}");
6471 result
6472 })
6473 }
6474
6475 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6476 self.promote_work_item_matching(|item: &WorkItem| match item {
6477 WorkItem::ResumeThread {
6478 thread: item_thread,
6479 ..
6480 }
6481 | WorkItem::GuestCall {
6482 call:
6483 GuestCall {
6484 thread: item_thread,
6485 ..
6486 },
6487 ..
6488 } => *item_thread == thread,
6489 _ => false,
6490 })
6491 }
6492
6493 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6494 where
6495 F: FnMut(&WorkItem) -> bool,
6496 {
6497 if self.switch_item.is_none() {
6504 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6505 self.switch_item = self.high_priority.remove(index);
6506 }
6507 }
6508 if self.switch_item.is_none() {
6509 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6510 self.switch_item = self.low_priority.remove(index);
6511 }
6512 }
6513 Ok(self.switch_item.is_some())
6514 }
6515
6516 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6519 match task {
6520 Scope::HostId(task) => {
6521 let task: TableId<HostTask> = TableId::new(task);
6522 Ok(&mut self.get_mut(task)?.call_context)
6523 }
6524 Scope::Id(task) => {
6525 let task: TableId<GuestTask> = TableId::new(task);
6526 Ok(&mut self.get_mut(task)?.call_context)
6527 }
6528 }
6529 }
6530
6531 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6532 self.deferred_host_call_context.as_mut()
6533 }
6534
6535 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6536 match self.futures.get_mut().as_mut() {
6537 Some(f) => Ok(f),
6538 None => bail_bug!("futures field of concurrent state is currently taken"),
6539 }
6540 }
6541
6542 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6543 self.table.get_mut()
6544 }
6545
6546 fn debug_assert_deferred_host_invariant(&self) {
6547 debug_assert_eq!(
6548 self.deferred_host_call_context.is_some(),
6549 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6550 "a deferred host thread and call context must exist together",
6551 );
6552 }
6553
6554 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6555 self.debug_assert_deferred_host_invariant();
6556 let caller = match self.unforced_current_thread {
6557 CurrentThread::DeferredHost(caller) => caller,
6558 thread => return Ok(thread),
6559 };
6560
6561 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6563 let task = self.push(task)?;
6564 let call_context = self
6565 .deferred_host_call_context
6566 .take()
6567 .expect("deferred host call context should be present");
6568 self.get_mut(task)
6569 .expect("newly inserted host task should be present")
6570 .call_context = call_context;
6571 self.unforced_current_thread = CurrentThread::Host(task);
6572 self.debug_assert_deferred_host_invariant();
6573 log::trace!("new host task materialized {task:?}");
6574 Ok(CurrentThread::Host(task))
6575 }
6576
6577 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6578 match self.materialize_host_task()? {
6579 CurrentThread::Host(id) => Ok(Some(id)),
6580 CurrentThread::None => Ok(None),
6581 CurrentThread::Guest(_) => {
6582 bail_bug!("tried to materialize a host task id from a guest thread")
6583 }
6584 CurrentThread::DeferredHost(_) => {
6585 bail_bug!(
6586 "current thread is a deferred host thread which should have been materialized"
6587 )
6588 }
6589 }
6590 }
6591
6592 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6593 match self.materialize_host_task()? {
6594 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6595 _ => bail_bug!("current scope is not a deferred host scope"),
6596 }
6597 }
6598
6599 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6600 let count = &mut self.get_mut(task)?.ref_count;
6601 *count += 1;
6602 log::trace!("increment {task:?} to {count}");
6603 Ok(())
6604 }
6605
6606 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6607 let count = &mut self.get_mut(task)?.ref_count;
6608 if *count == 0 {
6609 bail_bug!("can't decrement task ref count that's already zero");
6610 }
6611 *count -= 1;
6612 log::trace!("decrement {task:?} to {count}");
6613 if *count == 0 {
6614 let task = self.delete(task)?;
6615
6616 self.decrement_group_ref_count(task.group)?;
6617
6618 debug_assert!(task.decremented_interesting_task_count);
6625 }
6626 Ok(())
6627 }
6628
6629 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6630 log::trace!("delete host task {task:?}");
6631 let task = self.delete(task)?;
6632
6633 self.decrement_group_ref_count(task.group)
6634 }
6635}
6636
6637fn for_any_lower<
6640 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6641>(
6642 fun: F,
6643) -> F {
6644 fun
6645}
6646
6647fn for_any_lift<
6649 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6650>(
6651 fun: F,
6652) -> F {
6653 fun
6654}
6655
6656fn check_ambient_store(id: StoreId) {
6657 let message = "\
6658 `Future`s which depend on asynchronous component tasks, streams, or \
6659 futures to complete may only be polled from the event loop of the \
6660 store to which they belong. Please use \
6661 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6662 ";
6663 tls::try_get(|store| {
6664 let matched = match store {
6665 tls::TryGet::Some(store) => store.id() == id,
6666 tls::TryGet::Taken | tls::TryGet::None => false,
6667 };
6668
6669 if !matched {
6670 panic!("{message}")
6671 }
6672 });
6673}
6674
6675fn unpack_callback_code(code: u32) -> (u32, u32) {
6676 (code & 0xF, code >> 4)
6677}
6678
6679struct WaitableCheckParams {
6683 set: TableId<WaitableSet>,
6684 options: OptionsIndex,
6685 payload: u32,
6686}
6687
6688enum WaitableCheck {
6691 Wait,
6692 Poll,
6693}
6694
6695pub(crate) struct PreparedCall<R> {
6697 handle: Func,
6699 thread: QualifiedThreadId,
6701 param_count: usize,
6703 rx: oneshot::Receiver<LiftedResult>,
6706 _phantom: PhantomData<R>,
6707}
6708
6709impl<R> PreparedCall<R> {
6710 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6717 store
6718 .concurrent_state_mut()?
6719 .increment_task_ref_count(self.thread.task)?;
6720
6721 Ok(TaskId {
6722 task: self.thread.task,
6723 })
6724 }
6725}
6726
6727pub(crate) struct TaskId {
6729 task: TableId<GuestTask>,
6730}
6731
6732impl TaskId {
6733 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6745 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6746 if !task.already_lowered_parameters() {
6747 task.lower_params = Some(Box::new(|store, _| {
6752 store.set_trapped();
6753 bail!("the host future for this call was dropped before it started")
6754 }));
6755 } else {
6756 _ = store
6761 .concurrent_state_mut()?
6762 .decrement_task_ref_count(self.task);
6763 }
6764
6765 Ok(())
6766 }
6767}
6768
6769pub(crate) fn prepare_call<T, R>(
6775 mut store: StoreContextMut<T>,
6776 handle: Func,
6777 param_count: usize,
6778 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6779 + Send
6780 + Sync
6781 + 'static,
6782 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6783 + Send
6784 + Sync
6785 + 'static,
6786) -> Result<PreparedCall<R>> {
6787 if !store.0.may_enter() {
6788 bail!(Trap::CannotEnterComponent);
6789 }
6790
6791 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6792
6793 let instance = handle.instance().id().get(store.0);
6794 let options = &instance.component().env_component().options[options];
6795 let ty = &instance.component().types()[ty];
6796 let async_typed = ty.async_;
6797 let async_lifted = raw_options.async_;
6798 let task_return_type = ty.results;
6799 let component_instance = raw_options.instance;
6800 let callback = options.callback.map(|i| instance.runtime_callback(i));
6801 let memory = options
6802 .memory()
6803 .map(|i| instance.runtime_memory(i))
6804 .map(SendSyncPtr::new);
6805 let string_encoding = options.string_encoding;
6806 let token = StoreToken::new(store.as_context_mut());
6807 let caller = store.0.materialize_host_task_id()?;
6808 let state = store.0.concurrent_state_mut()?;
6809
6810 let (tx, rx) = oneshot::channel();
6811
6812 let instance = handle.instance().runtime_instance(component_instance);
6813 let thread = GuestTask::new(
6814 state,
6815 Box::new(for_any_lower(move |store, params| {
6816 lower_params(token.as_context_mut(store), params)
6817 })),
6818 LiftResult {
6819 lift: Box::new(for_any_lift(move |store, result| {
6820 lift_result(store, result)
6821 })),
6822 ty: task_return_type,
6823 memory,
6824 string_encoding,
6825 },
6826 Caller::Host {
6827 tx: Some(tx),
6828 caller,
6829 },
6830 callback.map(|callback| {
6831 let callback = SendSyncPtr::new(callback);
6832 let instance = handle.instance();
6833 Box::new(move |store: &mut dyn VMStore, event, handle| {
6834 let store = token.as_context_mut(store);
6835 unsafe { instance.call_callback(store, callback, event, handle) }
6838 }) as CallbackFn
6839 }),
6840 instance,
6841 async_typed,
6842 async_lifted,
6843 )?;
6844
6845 Ok(PreparedCall {
6846 handle,
6847 thread,
6848 param_count,
6849 rx,
6850 _phantom: PhantomData,
6851 })
6852}
6853
6854pub(crate) struct StagedCall<R> {
6855 store: StoreId,
6856 rx: oneshot::Receiver<LiftedResult>,
6857 _marker: PhantomData<fn() -> R>,
6858 group: TaskGroupId,
6866}
6867
6868impl<R> StagedCall<R> {
6869 pub(crate) fn new<T: 'static>(
6880 mut store: StoreContextMut<T>,
6881 prepared: PreparedCall<R>,
6882 ) -> Result<StagedCall<R>> {
6883 let PreparedCall {
6884 handle,
6885 thread,
6886 param_count,
6887 rx,
6888 ..
6889 } = prepared;
6890
6891 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6892
6893 let state = store.0.concurrent_state_mut()?;
6894 let group = state.get_mut(thread.task)?.group;
6895 state.decrement_task_ref_count(thread.task)?;
6896
6897 Ok(StagedCall {
6898 store: store.0.id(),
6899 rx,
6900 _marker: PhantomData,
6901 group,
6902 })
6903 }
6904}
6905
6906impl<R> Future for StagedCall<R>
6907where
6908 R: 'static,
6909{
6910 type Output = Result<R>;
6911
6912 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6913 check_ambient_store(self.store);
6914 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6915 Ok(r) => match r.downcast() {
6916 Ok(r) => Ok(*r),
6917 Err(_) => bail_bug!("wrong type of value produced"),
6918 },
6919 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6920 })
6921 }
6922}
6923
6924fn stage_call0<T: 'static>(
6927 store: StoreContextMut<T>,
6928 handle: Func,
6929 guest_thread: QualifiedThreadId,
6930 param_count: usize,
6931) -> Result<()> {
6932 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6933 let is_concurrent = raw_options.async_;
6934 let callback = raw_options.callback;
6935 let instance = handle.instance();
6936 let callee = handle.lifted_core_func(store.0);
6937 let post_return = raw_options
6938 .post_return
6939 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6940 let callback = callback.map(|i| {
6941 let instance = instance.id().get(store.0);
6942 SendSyncPtr::new(instance.runtime_callback(i))
6943 });
6944
6945 log::trace!("queueing call {guest_thread:?}");
6946
6947 unsafe {
6951 instance.stage_call(
6952 store,
6953 guest_thread,
6954 SendSyncPtr::new(callee),
6955 param_count,
6956 1,
6957 is_concurrent,
6958 callback,
6959 post_return.map(SendSyncPtr::new),
6960 true,
6961 )
6962 }
6963}
6964
6965#[cfg(all(test, feature = "cranelift", feature = "wat"))]
6966mod tests {
6967 use super::*;
6968 use crate::component::{Component, Linker};
6969 use crate::store::AsStoreOpaque;
6970 use crate::{Config, Engine};
6971
6972 fn host_subtask(
6973 state: HostTaskState,
6974 event: Option<Event>,
6975 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
6976 let mut config = Config::new();
6977 config.wasm_component_model_async(true);
6978 let engine = Engine::new(&config)?;
6979 let component = Component::new(&engine, "(component)")?;
6980 let mut store = Store::new(&engine, ());
6981 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
6982 let store_opaque = store.as_store_opaque();
6983 let concurrent_state = store_opaque.concurrent_state_mut()?;
6984 let group = concurrent_state.make_task_group()?;
6987 let task = concurrent_state.push(HostTask {
6988 common: WaitableCommon::default(),
6989 call_context: CallContext::default(),
6990 state,
6991 group,
6992 })?;
6993 let (state, counter) = store_opaque.instance_state_and_handle_counter(
6994 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
6995 );
6996 let handle = state
6997 .handle_table()
6998 .subtask_insert_host(counter, task.rep())?;
6999 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7000 common.handle = Some(handle);
7001 common.event = event;
7002 Ok((store, instance, task, handle))
7003 }
7004
7005 #[test]
7006 fn host_subtask_drop_during_cancellation() -> Result<()> {
7007 for abort_completed in [false, true] {
7008 let (handle, future) = JoinHandle::run(future::pending::<()>());
7009 let mut future = pin!(future);
7010 let (mut store, instance, task, handle) =
7011 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7012 let store = store.as_store_opaque();
7013 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7014 assert_eq!(
7015 instance.subtask_cancel(store, caller, true, handle)?,
7016 BLOCKED
7017 );
7018 if abort_completed {
7019 assert!(matches!(
7022 future
7023 .as_mut()
7024 .poll(&mut Context::from_waker(Waker::noop())),
7025 Poll::Ready(None),
7026 ));
7027 }
7028 for async_ in [false, true] {
7029 let err = instance
7030 .subtask_cancel(store, caller, async_, handle)
7031 .unwrap_err();
7032 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7033 }
7034 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7035 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7036 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7037 }
7038 Ok(())
7039 }
7040
7041 #[test]
7042 fn host_subtask_cancel_after_completion() -> Result<()> {
7043 for async_ in [false, true] {
7044 let (mut store, instance, task, handle) = host_subtask(
7045 HostTaskState::CalleeDone { cancelled: false },
7046 Some(Event::Subtask {
7047 status: Status::Returned,
7048 }),
7049 )?;
7050 let store = store.as_store_opaque();
7051 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7052 assert_eq!(
7053 instance.subtask_cancel(store, caller, async_, handle)?,
7054 Status::Returned as u32,
7055 );
7056 let err = instance
7057 .subtask_cancel(store, caller, async_, handle)
7058 .unwrap_err();
7059 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7060 instance.subtask_drop(store, caller, handle)?;
7061 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7062 }
7063 Ok(())
7064 }
7065
7066 #[test]
7067 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7068 for (cancelled, status) in [
7069 (false, Status::Returned),
7070 (true, Status::Returned),
7071 (true, Status::ReturnCancelled),
7072 ] {
7073 for delivered in [false, true] {
7074 let event = if delivered {
7075 None
7076 } else {
7077 Some(Event::Subtask { status })
7078 };
7079 let (mut store, instance, task, handle) =
7080 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7081 let store = store.as_store_opaque();
7082 let result = instance.subtask_drop(
7083 store,
7084 RuntimeComponentInstanceIndex::from_u32(0),
7085 handle,
7086 );
7087 if delivered {
7088 result?;
7089 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7090 } else {
7091 let err = result.unwrap_err();
7092 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7093 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7094 }
7095 }
7096 }
7097 Ok(())
7098 }
7099}