1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::store::ComponentInstanceId;
56use crate::component::{
57 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
58};
59use crate::fiber::{self, StoreFiber, StoreFiberYield};
60use crate::hash_set::HashSet;
61#[cfg(feature = "gc")]
62use crate::module::ModuleRegistry;
63use crate::prelude::*;
64use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
65#[cfg(feature = "gc")]
66use crate::vm::GcRootsList;
67use crate::vm::component::{
68 CallContext, ComponentInstance, CurrentScope, HandleCounter, InstanceState, Scope,
69};
70use crate::vm::{
71 AlwaysMut, SendSyncPtr, UncaughtException, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore,
72};
73use crate::{AsContext, AsContextMut, Result, StoreContext, StoreContextMut, ValRaw, bail};
74use crate::{Instance as ModuleInstance, bail_bug};
75use alloc::borrow::ToOwned;
76use alloc::collections::btree_map::Entry;
77use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
78use core::any::Any;
79use core::cell::UnsafeCell;
80use core::fmt;
81use core::future;
82use core::future::Future;
83use core::marker::PhantomData;
84use core::mem::{self, ManuallyDrop, MaybeUninit};
85use core::ops::DerefMut;
86use core::pin::{Pin, pin};
87use core::ptr::{self, NonNull};
88use core::task::{Context, Poll, Waker};
89use futures::channel::oneshot;
90use futures::stream::{FuturesUnordered, StreamExt};
91use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
92use smallvec::SmallVec;
93use table::{TableDebug, TableId};
94use wasmtime_environ::component::{
95 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
96 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
97 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
98 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
99 TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
100};
101use wasmtime_environ::packed_option::ReservedValue;
102use wasmtime_environ::{ModuleInternedTypeIndex, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
103#[cfg(feature = "gc")]
104use wasmtime_unwinder::Unwind;
105
106pub use abort::JoinHandle;
107pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
108pub use future_stream_any::{FutureAny, StreamAny};
109pub use futures_and_streams::{
110 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
111 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
112 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
113};
114pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
115#[cfg(feature = "task-group-hook")]
116pub use task_group_hook::TaskGroupHook;
117pub use task_group_hook::TaskGroupId;
118
119mod abort;
120mod error_contexts;
121mod func;
122mod future_stream_any;
123mod futures_and_streams;
124pub(crate) mod table;
125#[cfg(feature = "task-group-hook")]
126mod task_group_hook;
127#[cfg(not(feature = "task-group-hook"))]
128mod task_group_hook_disabled;
129#[cfg(not(feature = "task-group-hook"))]
130use task_group_hook_disabled as task_group_hook;
131pub(crate) mod tls;
132
133const BLOCKED: u32 = 0xffff_ffff;
136
137#[derive(Clone, Copy, Eq, PartialEq, Debug)]
139pub enum Status {
140 Starting = 0,
141 Started = 1,
142 Returned = 2,
143 StartCancelled = 3,
144 ReturnCancelled = 4,
145}
146
147impl Status {
148 pub fn pack(self, waitable: Option<u32>) -> u32 {
154 assert!(matches!(self, Status::Returned) == waitable.is_none());
155 let waitable = waitable.unwrap_or(0);
156 assert!(waitable < (1 << 28));
157 (waitable << 4) | (self as u32)
158 }
159}
160
161#[derive(Clone, Copy, Debug)]
164enum Event {
165 None,
166 Subtask {
167 status: Status,
168 },
169 StreamRead {
170 code: ReturnCode,
171 pending: Option<(TypeStreamTableIndex, u32)>,
172 },
173 StreamWrite {
174 code: ReturnCode,
175 pending: Option<(TypeStreamTableIndex, u32)>,
176 },
177 FutureRead {
178 code: ReturnCode,
179 pending: Option<(TypeFutureTableIndex, u32)>,
180 },
181 FutureWrite {
182 code: ReturnCode,
183 pending: Option<(TypeFutureTableIndex, u32)>,
184 },
185 Cancelled,
186}
187
188impl Event {
189 fn parts(self) -> (u32, u32) {
194 const EVENT_NONE: u32 = 0;
195 const EVENT_SUBTASK: u32 = 1;
196 const EVENT_STREAM_READ: u32 = 2;
197 const EVENT_STREAM_WRITE: u32 = 3;
198 const EVENT_FUTURE_READ: u32 = 4;
199 const EVENT_FUTURE_WRITE: u32 = 5;
200 const EVENT_CANCELLED: u32 = 6;
201 match self {
202 Event::None => (EVENT_NONE, 0),
203 Event::Cancelled => (EVENT_CANCELLED, 0),
204 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
205 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
206 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
207 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
208 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
209 }
210 }
211}
212
213mod callback_code {
215 pub const EXIT: u32 = 0;
216 pub const YIELD: u32 = 1;
217 pub const WAIT: u32 = 2;
218}
219
220const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
222const START_FLAG_ASYNC_CALLER: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLER as u32;
223
224pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
230 store: StoreContextMut<'a, T>,
231 get_data: fn(&mut T) -> D::Data<'_>,
232}
233
234impl<'a, T, D> Access<'a, T, D>
235where
236 D: HasData + ?Sized,
237 T: 'static,
238{
239 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
241 Self { store, get_data }
242 }
243
244 pub fn data_mut(&mut self) -> &mut T {
246 self.store.data_mut()
247 }
248
249 pub fn get(&mut self) -> D::Data<'_> {
251 (self.get_data)(self.data_mut())
252 }
253
254 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
258 where
259 T: 'static,
260 {
261 let accessor = Accessor {
262 get_data: self.get_data,
263 token: StoreToken::new(self.store.as_context_mut()),
264 };
265 self.store
266 .as_context_mut()
267 .spawn_with_accessor(accessor, task)
268 }
269
270 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
273 self.get_data
274 }
275}
276
277impl<'a, T, D> AsContext for Access<'a, T, D>
278where
279 D: HasData + ?Sized,
280 T: 'static,
281{
282 type Data = T;
283
284 fn as_context(&self) -> StoreContext<'_, T> {
285 self.store.as_context()
286 }
287}
288
289impl<'a, T, D> AsContextMut for Access<'a, T, D>
290where
291 D: HasData + ?Sized,
292 T: 'static,
293{
294 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
295 self.store.as_context_mut()
296 }
297}
298
299pub struct Accessor<T: 'static, D = HasSelf<T>>
359where
360 D: HasData + ?Sized,
361{
362 token: StoreToken<T>,
363 get_data: fn(&mut T) -> D::Data<'_>,
364}
365
366pub trait AsAccessor {
383 type Data: 'static;
385
386 type AccessorData: HasData + ?Sized;
389
390 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
392}
393
394impl<T: AsAccessor + ?Sized> AsAccessor for &T {
395 type Data = T::Data;
396 type AccessorData = T::AccessorData;
397
398 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
399 T::as_accessor(self)
400 }
401}
402
403impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
404 type Data = T;
405 type AccessorData = D;
406
407 fn as_accessor(&self) -> &Accessor<T, D> {
408 self
409 }
410}
411
412const _: () = {
435 const fn assert<T: Send + Sync>() {}
436 assert::<Accessor<UnsafeCell<u32>>>();
437};
438
439impl<T> Accessor<T> {
440 pub(crate) fn new(token: StoreToken<T>) -> Self {
449 Self {
450 token,
451 get_data: |x| x,
452 }
453 }
454}
455
456impl<T, D> Accessor<T, D>
457where
458 D: HasData + ?Sized,
459{
460 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
478 tls::get(|vmstore| {
479 fun(Access {
480 store: self.token.as_context_mut(vmstore),
481 get_data: self.get_data,
482 })
483 })
484 }
485
486 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
489 self.get_data
490 }
491
492 pub fn with_getter<D2: HasData>(
509 &self,
510 get_data: fn(&mut T) -> D2::Data<'_>,
511 ) -> Accessor<T, D2> {
512 Accessor {
513 token: self.token,
514 get_data,
515 }
516 }
517
518 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
534 where
535 T: 'static,
536 {
537 let accessor = self.clone_for_spawn();
538 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
539 }
540
541 fn clone_for_spawn(&self) -> Self {
542 Self {
543 token: self.token,
544 get_data: self.get_data,
545 }
546 }
547
548 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
584 self.with(|mut access| {
585 let store = access.as_context_mut().0;
586 let state = store.concurrent_state_mut_without_forcing_current_thread();
587 if state.interesting_tasks == 0 {
588 Poll::Ready(())
589 } else {
590 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
591 Poll::Pending
592 }
593 })
594 }
595
596 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
613 self.with(|mut access| {
614 let store = access.as_context_mut().0;
615 let (_, _, _, raw_options) = func.abi_info(store);
616 let instance = func.instance().runtime_instance(raw_options.instance);
617 let state = store.instance_state(instance).concurrent_state();
618 if state.backpressure == 0 {
619 Poll::Ready(())
620 } else {
621 store
622 .concurrent_state_mut_without_forcing_current_thread()
623 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
624 Poll::Pending
625 }
626 })
627 }
628}
629
630pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
652 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
653where
654 D: HasData + ?Sized,
655{
656 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
658}
659
660impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
661where
662 T: 'static,
663 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
664 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
665 Fut: Future<Output = Result<()>> + Send + 'fut,
666 D: HasData,
667{
668 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
669 (self)(accessor)
670 }
671}
672
673enum CallerInfo {
676 Async {
678 params: Vec<ValRaw>,
679 has_result: bool,
680 },
681 Sync {
683 params: Vec<ValRaw>,
684 result_count: u32,
685 },
686}
687
688enum WaitMode {
690 Fiber(StoreFiber<'static>),
692 Callback(Instance),
695}
696
697impl fmt::Debug for WaitMode {
698 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
699 match self {
700 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
701 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
702 }
703 }
704}
705
706#[derive(Debug)]
708enum SuspendReason {
709 Waiting {
712 set: TableId<WaitableSet>,
713 thread: QualifiedThreadId,
714 },
715 YieldingToSubtask { thread: QualifiedThreadId },
718 NeedWork,
721 Yielding { thread: QualifiedThreadId },
724 ExplicitlySuspending { thread: QualifiedThreadId },
727}
728
729enum GuestCallKind {
731 DeliverEvent {
734 instance: Instance,
736 set: Option<TableId<WaitableSet>>,
741 },
742 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
745 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
746}
747
748impl fmt::Debug for GuestCallKind {
749 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
750 match self {
751 Self::DeliverEvent { instance, set } => f
752 .debug_struct("DeliverEvent")
753 .field("instance", instance)
754 .field("set", set)
755 .finish(),
756 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
757 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
758 }
759 }
760}
761
762#[derive(Copy, Clone, Debug)]
764pub enum SuspensionTarget {
765 Resume(u32),
766 Promote(u32),
767 None,
768}
769
770#[derive(Copy, Clone, Debug)]
772pub enum ResumeThread {
773 Promote,
774 Resume,
775 ResumeLater,
776}
777
778#[derive(Debug)]
780struct GuestCall {
781 thread: QualifiedThreadId,
782 kind: GuestCallKind,
783}
784
785impl GuestCall {
786 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
797 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
798 let async_typed = task.async_typed;
799 let async_lifted = task.async_lifted;
800 let has_callback = task.callback.is_some();
801 let instance = task.instance;
802 let state = store.instance_state(instance).concurrent_state();
803
804 let ready = match &self.kind {
805 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
806 GuestCallKind::StartImplicit(_) => {
807 if async_typed {
808 let needs_exclusive = !async_lifted || has_callback;
811 !((needs_exclusive && state.do_not_enter) || state.backpressure > 0)
812 } else {
813 true
814 }
815 }
816 GuestCallKind::StartExplicit(_) => true,
817 };
818 log::trace!(
819 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
820 state.do_not_enter,
821 state.backpressure
822 );
823 Ok(ready)
824 }
825}
826
827enum WorkerItem {
829 GuestCall(GuestCall),
830 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
831}
832
833enum WorkItem {
836 PushFuture(AlwaysMut<HostTaskFuture>),
838 ResumeFiber {
840 instance: RuntimeInstance,
841 thread: QualifiedThreadId,
842 fiber: StoreFiber<'static>,
843 },
844 ResumeThread {
846 instance: RuntimeInstance,
847 thread: QualifiedThreadId,
848 },
849 GuestCall {
851 instance: RuntimeInstance,
852 call: GuestCall,
853 },
854 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
856}
857
858struct ResumingFiber<'a> {
864 store: &'a mut StoreOpaque,
865 fiber: Option<StoreFiber<'static>>,
866}
867
868impl Drop for ResumingFiber<'_> {
869 fn drop(&mut self) {
870 if let Some(mut fiber) = self.fiber.take() {
871 fiber.dispose(self.store);
872 }
873 }
874}
875
876impl fmt::Debug for WorkItem {
877 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
878 match self {
879 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
880 Self::ResumeFiber {
881 instance, thread, ..
882 } => f
883 .debug_struct("ResumeFiber")
884 .field("instance", instance)
885 .field("thread", thread)
886 .finish(),
887 Self::ResumeThread { instance, thread } => f
888 .debug_struct("ResumeThread")
889 .field("instance", instance)
890 .field("thread", thread)
891 .finish(),
892 Self::GuestCall { instance, call } => f
893 .debug_struct("GuestCall")
894 .field("instance", instance)
895 .field("call", call)
896 .finish(),
897 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
898 }
899 }
900}
901
902#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
904pub(crate) enum WaitResult {
905 Cancelled,
906 Completed,
907}
908
909pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
917 store: &mut dyn VMStore,
918 host_task: EnteredHostTask,
919 future: impl Future<Output = Result<R>> + Send + 'static,
920) -> Result<R> {
921 let mut future = Box::pin(future);
928 let poll = tls::set(store, || {
929 future
930 .as_mut()
931 .poll(&mut Context::from_waker(&Waker::noop()))
932 });
933
934 let caller = match host_task {
935 Some(caller) => caller,
936 None => bail_bug!("host task wasn't created but should have been"),
937 };
938
939 let task = match poll {
940 Poll::Ready(result) => return result,
942
943 Poll::Pending => {
948 let Some(task) = store.materialize_host_task_id()? else {
949 bail_bug!("current thread is not a host thread")
950 };
951
952 let future = Box::pin(async move {
955 let result = run_with_host_task_set(task, future).await??;
956 tls::get(move |store| {
957 let state = store.concurrent_state_mut()?;
958 let host_state = &mut state.get_mut(task)?.state;
959 assert!(matches!(host_state, HostTaskState::CalleeStarted));
960 *host_state = HostTaskState::CalleeFinished(Box::new(result));
961
962 Waitable::Host(task).set_event(
963 state,
964 Some(Event::Subtask {
965 status: Status::Returned,
966 }),
967 )?;
968
969 Ok(())
970 })
971 }) as HostTaskFuture;
972
973 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
974 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
975
976 let state = store.concurrent_state_mut()?;
977 state.push_future(future);
978
979 let set = state.get_mut(caller.thread)?.sync_call_set;
980 Waitable::Host(task).join(state, Some(set))?;
981
982 store.suspend(SuspendReason::Waiting {
983 set,
984 thread: caller,
985 })?;
986
987 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
991 task
992 }
993 };
994
995 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
997 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
998 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
999 Ok(result) => *result,
1000 Err(_) => bail_bug!("host task finished with wrong type of result"),
1001 }),
1002 _ => bail_bug!("unexpected host task state after completion"),
1003 }
1004}
1005
1006fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
1008 match call.kind {
1009 GuestCallKind::DeliverEvent { instance, set } => {
1010 if let Some(set) = set {
1013 store.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
1014 }
1015 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
1016 Some(pair) => pair,
1017 None => match set {
1018 Some(set) => {
1023 log::trace!(
1024 "event for {:?} on {set:?} no longer present; waiting again",
1025 call.thread
1026 );
1027 return instance.wait_with_callback(
1028 store.store_opaque_mut(),
1029 call.thread,
1030 set,
1031 );
1032 }
1033 None => (Event::None, None),
1038 },
1039 };
1040 let state = store.concurrent_state_mut()?;
1041 if let Some(set) = set
1046 && waitable.is_none()
1047 && !state.get_mut(set)?.ready.is_empty()
1048 {
1049 state.wake_waiter(set)?;
1050 }
1051 let task = state.get_mut(call.thread.task)?;
1052 let runtime_instance = task.instance;
1053 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
1054
1055 log::trace!(
1056 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
1057 call.thread,
1058 );
1059
1060 instance.handle_callback_code(store, call.thread, runtime_instance, |store| {
1061 invoke_callback(store, call.thread.task, event, handle)
1062 })?;
1063 }
1064 GuestCallKind::StartImplicit(fun) => {
1065 fun(store)?;
1066 }
1067 GuestCallKind::StartExplicit(fun) => {
1068 fun(store)?;
1069 }
1070 }
1071
1072 Ok(())
1073}
1074
1075fn invoke_callback(
1078 store: &mut dyn VMStore,
1079 task: TableId<GuestTask>,
1080 event: Event,
1081 handle: u32,
1082) -> Result<u32> {
1083 let Some(callback) = store.concurrent_state_mut()?.get_mut(task)?.callback.take() else {
1084 bail_bug!("guest task callback field not present")
1085 };
1086
1087 let code = callback(store, event, handle)?;
1088
1089 store.concurrent_state_mut()?.get_mut(task)?.callback = Some(callback);
1090
1091 Ok(code)
1092}
1093
1094impl<T> Store<T> {
1095 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1097 where
1098 T: Send + 'static,
1099 {
1100 ensure!(
1101 self.as_context().0.concurrency_support(),
1102 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1103 );
1104 self.as_context_mut().run_concurrent(fun).await
1105 }
1106
1107 #[doc(hidden)]
1108 pub fn assert_concurrent_state_empty(&mut self) {
1109 self.as_context_mut().assert_concurrent_state_empty();
1110 }
1111
1112 #[doc(hidden)]
1113 pub fn concurrent_state_table_size(&mut self) -> usize {
1114 self.as_context_mut().concurrent_state_table_size()
1115 }
1116
1117 pub fn spawn(
1119 &mut self,
1120 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1121 ) -> Result<JoinHandle>
1122 where
1123 T: 'static,
1124 {
1125 self.as_context_mut().spawn(task)
1126 }
1127}
1128
1129impl<T> StoreContextMut<'_, T> {
1130 #[doc(hidden)]
1141 pub fn assert_concurrent_state_empty(self) {
1142 let store = self.0;
1143 store
1144 .store_data_mut()
1145 .components
1146 .assert_instance_states_empty();
1147 let state = store.concurrent_state_mut().unwrap();
1148 assert!(
1149 state.table.get_mut().is_empty(),
1150 "non-empty table: {:?}",
1151 state.table.get_mut()
1152 );
1153 assert!(state.switch_item.is_none());
1154 assert!(state.next_switch_item.is_none());
1155 assert!(state.high_priority.is_empty());
1156 assert!(state.low_priority.is_empty());
1157 assert!(state.saved_next_switch_items.is_empty());
1158 assert!(state.unforced_current_thread.is_none());
1159 assert!(state.deferred_host_call_context.is_none());
1160 assert!(state.futures_mut().unwrap().is_empty());
1161 assert!(state.global_error_context_ref_counts.is_empty());
1162 }
1163
1164 #[doc(hidden)]
1169 pub fn concurrent_state_table_size(&mut self) -> usize {
1170 self.0.concurrent_state_mut().unwrap().table.get_mut().len()
1171 }
1172
1173 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1183 where
1184 T: 'static,
1185 {
1186 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1187 self.spawn_with_accessor(accessor, task)
1188 }
1189
1190 fn spawn_with_accessor<D>(
1193 self,
1194 accessor: Accessor<T, D>,
1195 task: impl for<'fut> AccessorTask<'fut, T, D>,
1196 ) -> Result<JoinHandle>
1197 where
1198 T: 'static,
1199 D: HasData + ?Sized,
1200 {
1201 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1205 self.0
1206 .concurrent_state_mut()?
1207 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1208 Ok(handle)
1209 }
1210
1211 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1295 where
1296 T: Send + 'static,
1297 {
1298 ensure!(
1299 self.0.concurrency_support(),
1300 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1301 );
1302 self.do_run_concurrent(fun, false).await
1303 }
1304
1305 pub(super) async fn run_concurrent_trap_on_idle<R>(
1306 self,
1307 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1308 ) -> Result<R> {
1309 self.do_run_concurrent(fun, true).await
1310 }
1311
1312 async fn do_run_concurrent<R>(
1313 mut self,
1314 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1315 trap_on_idle: bool,
1316 ) -> Result<R> {
1317 debug_assert!(self.0.concurrency_support());
1318 self.0.check_event_loop_not_running()?;
1319 let token = StoreToken::new(self.as_context_mut());
1320
1321 struct Dropper<'a, T: 'static, V> {
1322 store: StoreContextMut<'a, T>,
1323 value: ManuallyDrop<V>,
1324 }
1325
1326 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1327 fn drop(&mut self) {
1328 self.store
1329 .0
1330 .concurrent_state_mut_already_forced_current_thread()
1331 .event_loop_running = false;
1332
1333 tls::set(self.store.0, || {
1334 unsafe { ManuallyDrop::drop(&mut self.value) }
1339 });
1340 }
1341 }
1342
1343 let accessor = &Accessor::new(token);
1344 self.0
1345 .concurrent_state_mut_already_forced_current_thread()
1346 .event_loop_running = true;
1347 let dropper = &mut Dropper {
1348 store: self,
1349 value: ManuallyDrop::new(fun(accessor)),
1350 };
1351 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1353
1354 let result = dropper
1355 .store
1356 .as_context_mut()
1357 .poll_until(future, trap_on_idle)
1358 .await;
1359
1360 if result.is_err() {
1361 dropper.store.0.set_trapped();
1362 }
1363
1364 result
1365 }
1366
1367 async fn poll_until<R>(
1373 mut self,
1374 mut future: Pin<&mut impl Future<Output = R>>,
1375 trap_on_idle: bool,
1376 ) -> Result<R> {
1377 struct Reset<'a, T: 'static> {
1378 store: StoreContextMut<'a, T>,
1379 futures: Option<FuturesUnordered<HostTaskFuture>>,
1380 }
1381
1382 impl<'a, T> Drop for Reset<'a, T> {
1383 fn drop(&mut self) {
1384 if let Some(futures) = self.futures.take() {
1385 *self
1386 .store
1387 .0
1388 .concurrent_state_mut_already_forced_current_thread()
1389 .futures
1390 .get_mut() = Some(futures);
1391 }
1392 }
1393 }
1394
1395 const MAX_TURNS_WITHOUT_YIELD: usize = 128;
1399 let mut turns_without_yield = 0;
1400
1401 loop {
1402 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1406 let mut reset = Reset {
1407 store: self.as_context_mut(),
1408 futures,
1409 };
1410 let mut next = match reset.futures.as_mut() {
1411 Some(f) => pin!(f.next()),
1412 None => bail_bug!("concurrent state missing futures field"),
1413 };
1414
1415 enum PollResult<R> {
1416 Complete(R),
1417 ProcessWork {
1418 ready: Option<WorkItem>,
1419 low_priority: bool,
1420 },
1421 }
1422
1423 let result = future::poll_fn(|cx| {
1424 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1427 return Poll::Ready(Ok(PollResult::Complete(value)));
1428 }
1429
1430 if reset.store.0.component_trapped() {
1439 return Poll::Ready(Err(Trap::CannotEnterComponent.into()));
1440 }
1441
1442 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1446 Poll::Ready(Some(output)) => {
1447 match output {
1448 Err(e) => return Poll::Ready(Err(e)),
1449 Ok(()) => {}
1450 }
1451 Poll::Ready(true)
1452 }
1453 Poll::Ready(None) => Poll::Ready(false),
1454 Poll::Pending => Poll::Pending,
1455 };
1456
1457 let state = reset.store.0.concurrent_state_mut()?;
1472 let mut ready = state.switch_item.take();
1473 let mut low_priority = false;
1474 if ready.is_none() {
1475 ready = state.high_priority.pop_back();
1476 if ready.is_none() {
1477 ready = state.low_priority.pop_back();
1478 low_priority = true;
1479 }
1480 }
1481 if ready.is_some() {
1482 return Poll::Ready(Ok(PollResult::ProcessWork {
1483 ready,
1484 low_priority,
1485 }));
1486 }
1487
1488 return match next {
1492 Poll::Ready(true) => {
1493 Poll::Ready(Ok(PollResult::ProcessWork {
1499 ready: None,
1500 low_priority: false,
1501 }))
1502 }
1503 Poll::Ready(false) => {
1504 if let Poll::Ready(value) =
1508 tls::set(reset.store.0, || future.as_mut().poll(cx))
1509 {
1510 Poll::Ready(Ok(PollResult::Complete(value)))
1511 } else {
1512 if trap_on_idle {
1518 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1525 Trap::CannotBlockSyncTask.into()
1526 } else {
1527 Trap::AsyncDeadlock.into()
1529 }))
1530 } else {
1531 Poll::Pending
1535 }
1536 }
1537 }
1538 Poll::Pending => Poll::Pending,
1543 };
1544 })
1545 .await;
1546
1547 drop(reset);
1551
1552 match result? {
1553 PollResult::Complete(value) => break Ok(value),
1556 PollResult::ProcessWork {
1559 mut ready,
1560 low_priority,
1561 } => {
1562 if low_priority {
1590 if let Some(item) = ready.take() {
1591 self.0.concurrent_state_mut()?.low_priority.push_back(item);
1592 }
1593 self.0.yield_now().await;
1594 turns_without_yield = 0;
1595 ready = self.0.concurrent_state_mut()?.low_priority.pop_back();
1596 }
1597
1598 if let Some(item) = ready {
1599 self.as_context_mut().handle_work_item(item).await?;
1600 }
1601
1602 turns_without_yield += 1;
1603 if turns_without_yield == MAX_TURNS_WITHOUT_YIELD {
1604 turns_without_yield = 0;
1605 self.0.yield_now().await;
1606 }
1607 }
1608 }
1609 }
1610 }
1611
1612 fn handle_work_item(self, item: WorkItem) -> impl Future<Output = Result<()>> {
1614 let item = match item {
1618 WorkItem::ResumeFiber {
1619 instance,
1620 thread,
1621 fiber,
1622 } => {
1623 let future = self.0.resume_fiber(fiber);
1624 log::trace!(
1625 "handle work item ResumeFiber {{ instance: {instance:?}, thread: {thread:?} }}"
1626 );
1627 return futures::future::Either::Left(future);
1628 }
1629 item => item,
1630 };
1631 futures::future::Either::Right(async move {
1632 log::trace!("handle work item {item:?}");
1633 match item {
1634 WorkItem::PushFuture(future) => {
1635 self.0
1636 .concurrent_state_mut()?
1637 .futures_mut()?
1638 .push(future.into_inner());
1639 }
1640 WorkItem::ResumeFiber { .. } => unreachable!(),
1641 WorkItem::ResumeThread { thread, .. } => {
1642 let slot = &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state;
1643 if !matches!(slot, GuestThreadState::Ready { .. }) {
1644 bail_bug!("cannot resume non-pending thread {thread:?}");
1645 }
1646 let GuestThreadState::Ready { fiber } =
1647 mem::replace(slot, GuestThreadState::Running)
1648 else {
1649 unreachable!()
1650 };
1651 self.0.resume_fiber(fiber).await?;
1652 }
1653 WorkItem::GuestCall { call, .. } => {
1654 if call.is_ready(self.0)? {
1655 self.0
1656 .concurrent_state_mut()?
1657 .get_mut(call.thread.thread)?
1658 .wake_on_cancel = WakeOnCancel::None;
1659 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1660 } else {
1661 let state = self.0.concurrent_state_mut()?;
1662 let task = state.get_mut(call.thread.task)?;
1663 if !task.starting_sent {
1664 task.starting_sent = true;
1665 if let GuestCallKind::StartImplicit(_) = &call.kind {
1666 Waitable::Guest(call.thread.task).set_event(
1667 state,
1668 Some(Event::Subtask {
1669 status: Status::Starting,
1670 }),
1671 )?;
1672 }
1673 }
1674
1675 let instance = state.get_mut(call.thread.task)?.instance;
1676 if let GuestCallKind::DeliverEvent { set: Some(set), .. } = &call.kind {
1685 let state = self.0.concurrent_state_mut()?;
1686 if !state.get_mut(*set)?.ready.is_empty() {
1687 state.wake_waiter(*set)?;
1688 }
1689 }
1690 self.0
1691 .instance_state(instance)
1692 .concurrent_state()
1693 .pending
1694 .insert(call.thread, call.kind);
1695
1696 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1700 }
1701 }
1702 WorkItem::WorkerFunction(fun) => {
1703 self.run_on_worker(WorkerItem::Function(fun)).await?;
1704 }
1705 }
1706
1707 Ok(())
1708 })
1709 }
1710
1711 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1713 let state = self.0.concurrent_state_mut()?;
1714 if state.worker_item.is_some() {
1715 bail_bug!("worker item already set when taking worker fiber");
1716 }
1717 let worker = if let Some(fiber) = state.worker.take() {
1718 fiber
1719 } else {
1720 unsafe {
1739 fiber::make_fiber_unchecked(self.0, move |store| {
1740 loop {
1741 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1742 bail_bug!("worker_item not present when resuming fiber")
1743 };
1744 match item {
1745 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1746 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1747 }
1748
1749 store.suspend(SuspendReason::NeedWork)?;
1750 }
1751 })?
1752 }
1753 };
1754
1755 self.0
1758 .concurrent_state_mut_already_forced_current_thread()
1759 .worker_item = Some(item);
1760
1761 self.0.resume_fiber(worker).await
1762 }
1763
1764 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1769 where
1770 T: 'static,
1771 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1772 + Send
1773 + Sync
1774 + 'static,
1775 R: Send + Sync + 'static,
1776 {
1777 let token = StoreToken::new(self);
1778 async move {
1779 let mut accessor = Accessor::new(token);
1780 closure(&mut accessor).await
1781 }
1782 }
1783
1784 pub(crate) async fn start_instance(
1785 &mut self,
1786 instance: ModuleInstance,
1787 callee: Option<RuntimeInstance>,
1788 ) -> Result<ModuleInstance> {
1789 self.0.check_event_loop_not_running()?;
1791 let (tx, rx) = oneshot::channel();
1792 let token = StoreToken::new(self.as_context_mut());
1793 self.0.queue_task(move |store| {
1794 _ = tx.send(
1795 super::instance::start_raw(&mut token.as_context_mut(store), instance, callee)
1796 .map(|()| instance),
1797 );
1798 Ok(())
1799 })?;
1800 self.as_context_mut()
1801 .run_concurrent_trap_on_idle(async |_| {
1802 rx.await
1803 .map_err(|_| format_err!("oneshot channel canceled"))
1804 })
1805 .await??
1806 }
1807}
1808
1809pub type EnteredHostTask = Option<QualifiedThreadId>;
1816
1817impl StoreOpaque {
1818 pub(crate) fn check_event_loop_not_running(&mut self) -> Result<()> {
1819 if self
1820 .concurrent_state_mut_already_forced_current_thread()
1821 .event_loop_running
1822 {
1823 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1824 }
1825 Ok(())
1826 }
1827
1828 #[inline]
1832 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1833 if !self.concurrency_support() {
1835 return Ok(CurrentThread::None);
1836 }
1837
1838 if !self
1841 .vm_store_context_mut()
1842 .current_thread_mut()
1843 .is_deferred()
1844 {
1845 return Ok(self
1846 .concurrent_state_mut_already_forced_current_thread()
1847 .unforced_current_thread);
1848 }
1849
1850 self.force_deferred_current_thread()
1851 }
1852
1853 #[cold]
1856 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1857 let state = self.concurrent_state_mut_without_forcing_current_thread();
1866 let id = match state.unforced_current_thread.guest_task() {
1867 Some(task) => state.get_mut(task)?.instance.instance,
1868 None => bail_bug!("deferred component-model thread with non-guest base"),
1869 };
1870
1871 let mut frames = SmallVec::<[_; 8]>::new();
1874 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1875 while let Some(ptr) = cur.as_deferred() {
1876 let deferred = unsafe { ptr.as_non_null().as_ref() };
1881 frames.push((
1882 deferred.callee_async != 0,
1883 deferred.callee_instance,
1884 deferred.saved_context,
1885 deferred.lazy_lenders_start,
1886 ));
1887 cur = deferred.parent;
1888 }
1889
1890 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1894
1895 let current_context = *self.vm_store_context_mut().component_context_mut();
1898
1899 let lazy_lenders_end = *self.vm_store_context_mut().lazy_lenders_len_mut();
1906 frames.reverse();
1907 let lazy_lenders_base = match frames.first() {
1908 Some((_, _, _, start)) => *start,
1909 None => bail_bug!("forcing a deferred thread with no deferred frames"),
1910 };
1911 let lazy_lender_ends = frames
1912 .iter()
1913 .skip(1)
1914 .map(|(_, _, _, start)| *start)
1915 .chain([lazy_lenders_end])
1916 .collect::<SmallVec<[_; 8]>>();
1917 for ((callee_async, callee_instance, saved_context, start), lenders_end) in
1918 frames.into_iter().zip(lazy_lender_ends)
1919 {
1920 *self.vm_store_context_mut().component_context_mut() = saved_context;
1924 let callee = RuntimeInstance {
1925 instance: id,
1926 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1927 };
1928 self.enter_guest_sync_call(callee_async, callee)?;
1929
1930 self.promote_current_lazy_lends(id, start, lenders_end)?;
1933 }
1934
1935 debug_assert_eq!(
1938 *self.vm_store_context_mut().lazy_lenders_len_mut(),
1939 lazy_lenders_end
1940 );
1941 *self.vm_store_context_mut().lazy_lenders_len_mut() = lazy_lenders_base;
1942
1943 *self.vm_store_context_mut().component_context_mut() = current_context;
1945
1946 Ok(self
1947 .concurrent_state_mut_without_forcing_current_thread()
1948 .unforced_current_thread)
1949 }
1950
1951 fn promote_current_lazy_lends(
1957 &mut self,
1958 id: ComponentInstanceId,
1959 start: usize,
1960 end: usize,
1961 ) -> Result<()> {
1962 if start == end {
1963 return Ok(());
1964 }
1965
1966 let scope = match self
1967 .concurrent_state_mut_without_forcing_current_thread()
1968 .unforced_current_thread
1969 .guest_task()
1970 {
1971 Some(task) => Scope::Id(task.rep()),
1972 None => bail_bug!("replayed deferred frame is not a guest thread"),
1973 };
1974 let instance = Instance::from_wasmtime(self, id);
1975 self.promote_lazy_lends(instance, scope, start, end)
1976 }
1977
1978 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1979 match self.current_thread()?.guest() {
1980 Some(id) => Ok(*id),
1981 None => bail_bug!("current thread is not a guest thread"),
1982 }
1983 }
1984
1985 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1989 match self.current_thread()? {
1990 CurrentThread::Host(id) => Ok(Some(id)),
1991 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1992 _ => bail_bug!("current thread is not a host thread"),
1993 }
1994 }
1995
1996 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1999 Ok(self
2000 .concurrent_state_mut()?
2001 .materialize_current_host_task_id()?)
2002 }
2003
2004 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
2005 log::trace!("enter sync-typed call {callee:?}");
2006 let state = self.instance_state(callee).concurrent_state();
2007 let old_do_not_suspend = state.do_not_suspend;
2008 state.do_not_suspend = true;
2009
2010 let thread = self.current_guest_thread()?;
2011 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2012 if thread.old_do_not_suspend.is_some() {
2013 bail_bug!("current thread already has `old_do_not_suspend` value");
2014 }
2015
2016 thread.old_do_not_suspend = Some(old_do_not_suspend);
2017
2018 Ok(())
2019 }
2020
2021 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
2022 log::trace!("exit sync-typed call {callee:?}");
2023 let thread = self.current_guest_thread()?;
2024 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2025 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
2026 bail_bug!("current thread missing `old_do_not_suspend` value");
2027 };
2028 let state = self.instance_state(callee).concurrent_state();
2029 state.do_not_suspend = old_do_not_suspend;
2030 Ok(())
2031 }
2032
2033 pub(crate) fn enter_guest_sync_call(
2045 &mut self,
2046 callee_async_typed: bool,
2047 callee: RuntimeInstance,
2048 ) -> Result<()> {
2049 log::trace!("enter sync-lifted call {callee:?}");
2050 if !self.concurrency_support() {
2051 return self.enter_call_not_concurrent();
2052 }
2053
2054 let state = self.concurrent_state_mut()?;
2058 let item = state.next_switch_item.take();
2059 state.saved_next_switch_items.push(item);
2060
2061 let thread = self.current_thread()?;
2062 let caller = if let Some(thread) = thread.guest() {
2063 Caller::Guest { thread: *thread }
2064 } else {
2065 Caller::Host {
2066 tx: None,
2067 caller: self.materialize_host_task_id()?,
2068 }
2069 };
2070
2071 let state = self.concurrent_state_mut()?;
2072 let guest_thread = GuestTask::new(
2073 state,
2074 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
2075 LiftResult {
2076 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
2077 ty: TypeTupleIndex::reserved_value(),
2078 memory: None,
2079 string_encoding: StringEncoding::Utf8,
2080 },
2081 caller,
2082 None,
2083 callee,
2084 callee_async_typed,
2085 false,
2086 )?;
2087
2088 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
2089 guest_thread.thread,
2090 self,
2091 callee.index,
2092 )?;
2093 self.set_thread(guest_thread)?;
2094
2095 if !callee_async_typed {
2096 self.enter_sync_call(callee)?;
2097 }
2098
2099 Ok(())
2100 }
2101
2102 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
2110 if !self.concurrency_support() {
2111 return Ok(self.exit_call_not_concurrent());
2112 }
2113
2114 let thread = match self.current_thread()?.guest() {
2115 Some(t) => *t,
2116 None => bail_bug!("expected task when exiting"),
2117 };
2118 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
2119 let instance = task.instance;
2120
2121 let caller = match &task.caller {
2122 &Caller::Guest { thread } => thread.into(),
2123 &Caller::Host { caller, .. } => caller
2124 .map(CurrentThread::Host)
2125 .unwrap_or(CurrentThread::None),
2126 };
2127 task.lift_result = None;
2128 task.exited = true;
2129 let async_typed = task.async_typed;
2130
2131 if !async_typed {
2132 self.exit_sync_call(instance)?;
2133 }
2134
2135 self.set_thread(caller)?;
2136
2137 log::trace!("exit sync-lifted call {instance:?}");
2138
2139 if async_typed {
2140 self.switch_or_trap_if_may_not_suspend(instance)?;
2145 }
2146
2147 self.cleanup_thread(thread, instance)?;
2148
2149 self.concurrent_state_mut()?
2150 .decrement_task_ref_count(thread.task)?;
2151
2152 let state = self.concurrent_state_mut()?;
2153 let Some(item) = state.saved_next_switch_items.pop() else {
2154 bail_bug!("unable to pop from `saved_next_switch_items`");
2155 };
2156 if let Some(item) = mem::replace(&mut state.next_switch_item, item) {
2157 state.push_high_priority(item);
2160 bail_bug!("`next_switch_item` unexpectedly already set");
2161 }
2162
2163 Ok(())
2164 }
2165
2166 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2173 if !self.concurrency_support() {
2174 self.enter_call_not_concurrent()?;
2175 return Ok(None);
2176 }
2177 let caller = self.current_guest_thread()?;
2178 log::trace!("new deferred host task with caller {caller:?}");
2179
2180 self.set_thread(CurrentThread::DeferredHost(caller))?;
2181 let state = self.concurrent_state_mut()?;
2182 debug_assert!(state.deferred_host_call_context.is_none());
2183 state.deferred_host_call_context = Some(CallContext::default());
2184 state.debug_assert_deferred_host_invariant();
2185 Ok(Some(caller))
2186 }
2187
2188 pub(crate) fn host_task_delete(
2195 &mut self,
2196 original_task: EnteredHostTask,
2197 materialized_task: Option<TableId<HostTask>>,
2198 ) -> Result<()> {
2199 match original_task {
2200 Some(caller) => {
2201 self.set_thread(caller)?;
2202 if materialized_task.is_none() {
2203 let state = self.concurrent_state_mut()?;
2204 let context = state
2205 .deferred_host_call_context
2206 .take()
2207 .expect("deferred host call context should be present");
2208 debug_assert!(context.is_empty());
2209 state.debug_assert_deferred_host_invariant();
2210 }
2211 log::trace!(
2212 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2213 );
2214 if let Some(task) = materialized_task {
2215 self.concurrent_state_mut()?.delete_host_task(task)?;
2216 }
2217 }
2218 None => {
2219 debug_assert!(materialized_task.is_none());
2220 self.exit_call_not_concurrent();
2221 }
2222 }
2223 Ok(())
2224 }
2225
2226 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2229 self.component_instance_mut(instance.instance)
2230 .instance_state(instance.index)
2231 }
2232
2233 fn instance_state_and_handle_counter(
2236 &mut self,
2237 instance: RuntimeInstance,
2238 ) -> (&mut InstanceState, &mut HandleCounter) {
2239 let (component, counter) = self
2240 .store_data_mut()
2241 .component_instance_and_handle_counter_mut(instance.instance);
2242 (component.instance_state(instance.index), counter)
2243 }
2244
2245 pub(crate) fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2251 let thread = thread.into();
2252 let state = self.concurrent_state_mut()?;
2253 state.debug_assert_deferred_host_invariant();
2254 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2255
2256 state.handle_thread_switch(old_thread, thread)?;
2257
2258 if let Some(old_thread) = old_thread.guest() {
2266 let old_context = *self.vm_store_context_mut().component_context_mut();
2267 self.concurrent_state_mut()?
2268 .get_mut(old_thread.thread)?
2269 .context = old_context;
2270 }
2271 if cfg!(debug_assertions) {
2272 *self.vm_store_context_mut().component_context_mut() =
2273 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2274 }
2275 if let Some(thread) = thread.guest() {
2276 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2277 let context = thread.context;
2278 if cfg!(debug_assertions) {
2279 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2280 }
2281 *self.vm_store_context_mut().component_context_mut() = context;
2282 }
2283
2284 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2286 VMLazyThread::none()
2287 } else {
2288 VMLazyThread::forced()
2289 };
2290
2291 Ok(old_thread)
2292 }
2293
2294 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2296 if self.switch_if_may_not_suspend(instance)? {
2297 Ok(())
2298 } else {
2299 Err(Trap::CannotBlockSyncTask.into())
2300 }
2301 }
2302
2303 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2307 self.concurrent_state_mut()?;
2311
2312 Ok(!self.concurrency_support()
2313 || !self
2314 .instance_state(instance)
2315 .concurrent_state()
2316 .do_not_suspend
2317 || self
2318 .concurrent_state_mut()?
2319 .promote_instance_local_thread_work_item(instance)?)
2320 }
2321
2322 fn enter_instance(&mut self, instance: RuntimeInstance) {
2326 log::trace!("enter {instance:?}");
2327 self.instance_state(instance)
2328 .concurrent_state()
2329 .do_not_enter = true;
2330 }
2331
2332 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2336 log::trace!("exit {instance:?}");
2337 self.instance_state(instance)
2338 .concurrent_state()
2339 .do_not_enter = false;
2340 self.partition_pending(instance)
2341 }
2342
2343 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2351 for (thread, kind) in
2352 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2353 {
2354 let call = GuestCall { thread, kind };
2355 if call.is_ready(self)? {
2356 self.concurrent_state_mut()?
2357 .push_high_priority(WorkItem::GuestCall { instance, call });
2358 } else {
2359 self.instance_state(instance)
2360 .concurrent_state()
2361 .pending
2362 .insert(call.thread, call.kind);
2363 }
2364 }
2365
2366 if let Some(waker) = self
2367 .concurrent_state_mut()?
2368 .ready_for_concurrent_call_waker
2369 .take()
2370 {
2371 waker.wake();
2372 }
2373
2374 Ok(())
2375 }
2376
2377 pub(crate) fn backpressure_modify(
2379 &mut self,
2380 caller_instance: RuntimeInstance,
2381 modify: impl FnOnce(u16) -> Option<u16>,
2382 ) -> Result<()> {
2383 let state = self.instance_state(caller_instance).concurrent_state();
2384 let old = state.backpressure;
2385 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2386 state.backpressure = new;
2387
2388 if old > 0 && new == 0 {
2389 self.partition_pending(caller_instance)?;
2392 }
2393
2394 Ok(())
2395 }
2396
2397 fn resume_fiber(
2400 &mut self,
2401 fiber: StoreFiber<'static>,
2402 ) -> impl Future<Output = Result<()>> + '_ {
2403 let mut resuming = ResumingFiber {
2404 store: self,
2405 fiber: Some(fiber),
2406 };
2407 async move {
2408 let old_thread = resuming.store.current_thread()?;
2409 log::trace!("resume_fiber: save current thread {old_thread:?}");
2410
2411 let fiber = resuming.fiber.take().unwrap();
2412 resuming.fiber = fiber::resolve_or_release(resuming.store, fiber).await?;
2413
2414 resuming.store.set_thread(old_thread)?;
2415
2416 let state = resuming.store.concurrent_state_mut()?;
2417
2418 if let Some(ot) = old_thread.guest() {
2419 state.set_thread_running(ot.thread)?;
2420 }
2421 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2422
2423 if resuming.fiber.is_some() {
2424 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2425 let reason = match state.suspend_reason.take() {
2427 Some(r) => r,
2428 None => bail_bug!("suspend reason missing when resuming fiber"),
2429 };
2430 match reason {
2431 SuspendReason::NeedWork => {
2432 if state.worker.is_none() {
2433 state.worker = resuming.fiber.take();
2434 }
2435 }
2436 SuspendReason::Yielding { thread } => {
2437 let slot = &mut state.get_mut(thread.thread)?.state;
2438 slot.check_no_fiber()?;
2439 *slot = GuestThreadState::Ready {
2440 fiber: resuming.fiber.take().unwrap(),
2441 };
2442 let instance = state.get_mut(thread.task)?.instance;
2443 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2444 }
2445 SuspendReason::ExplicitlySuspending { thread } => {
2446 let slot = &mut state.get_mut(thread.thread)?.state;
2447 slot.check_no_fiber()?;
2448 *slot = GuestThreadState::Suspended(resuming.fiber.take().unwrap());
2449 }
2450 SuspendReason::Waiting { set, thread } => {
2451 match state.get_mut(set)?.waiting.entry(thread) {
2452 Entry::Vacant(entry) => {
2453 entry.insert(WaitMode::Fiber(resuming.fiber.take().unwrap()));
2454 }
2455 Entry::Occupied(_) => {
2456 bail_bug!("entry unexpectedly already exists for {thread:?}");
2457 }
2458 }
2459 }
2460 SuspendReason::YieldingToSubtask { thread } => {
2461 let instance = state.get_mut(thread.task)?.instance;
2470 if state.next_switch_item.is_some() {
2471 bail_bug!(
2474 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2475 a thread wanted to wait on a subtask"
2476 );
2477 }
2478 let item = state.next_switch_item.insert(WorkItem::ResumeFiber {
2479 instance,
2480 thread,
2481 fiber: resuming.fiber.take().unwrap(),
2482 });
2483 log::trace!("set next switch item to {item:?}");
2484 }
2485 };
2486 } else {
2487 log::trace!("resume_fiber: fiber has exited");
2488 }
2489
2490 Ok(())
2491 }
2492 }
2493
2494 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2500 log::trace!("suspend fiber: {reason:?}");
2501
2502 if !self.fiber_async_state_mut().can_block() {
2505 return Err(format_err!("future dropped"));
2506 }
2507
2508 let state = self.concurrent_state_mut()?;
2509
2510 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2517 SuspendReason::Yielding { .. }
2518 | SuspendReason::Waiting { .. }
2519 | SuspendReason::ExplicitlySuspending { .. } => {
2520 if state.switch_item.is_none() {
2523 state.take_next_switch_item()?;
2524 }
2525
2526 (true, false)
2527 }
2528 SuspendReason::YieldingToSubtask { .. } => (true, true),
2529 SuspendReason::NeedWork => (false, false),
2530 };
2531
2532 let old_next_switch_item = if save_and_restore_next_switch_item {
2533 Some(state.save_next_switch_item()?)
2534 } else {
2535 None
2536 };
2537
2538 let old_guest_thread = if save_and_restore_thread {
2539 self.current_thread()?
2540 } else {
2541 CurrentThread::None
2542 };
2543
2544 let waiting_set = match &reason {
2545 SuspendReason::Waiting { set, .. } => Some(*set),
2546 _ => None,
2547 };
2548
2549 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2550 if suspend_reason.is_some() {
2551 bail_bug!("suspend reason unexpectedly already set for store");
2552 }
2553 *suspend_reason = Some(reason);
2554
2555 if let Some(set) = waiting_set {
2558 self.concurrent_state_mut()?.get_mut(set)?.num_waiting += 1;
2559 }
2560
2561 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2562
2563 if let Some(set) = waiting_set {
2564 self.concurrent_state_mut()?.get_mut(set)?.stop_waiting()?;
2565 }
2566
2567 if save_and_restore_thread {
2568 self.set_thread(old_guest_thread)?;
2569 }
2570
2571 if let Some(item) = old_next_switch_item {
2572 let state = self.concurrent_state_mut()?;
2573 state.restore_next_switch_item(item)?;
2574 }
2575
2576 Ok(())
2577 }
2578
2579 fn wait_for_event(
2580 &mut self,
2581 caller_instance: RuntimeInstance,
2582 waitable: Waitable,
2583 ) -> Result<()> {
2584 let caller = self.current_guest_thread()?;
2585 let state = self.concurrent_state_mut()?;
2586
2587 waitable.trap_if_in_waitable_set(state)?;
2588
2589 let set = state.get_mut(caller.thread)?.sync_call_set;
2590 waitable.join(state, Some(set))?;
2591
2592 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2593
2594 self.suspend(SuspendReason::Waiting {
2595 set,
2596 thread: caller,
2597 })?;
2598 let state = self.concurrent_state_mut()?;
2599
2600 waitable.join(state, None)
2601 }
2602
2603 fn cleanup_thread(
2625 &mut self,
2626 guest_thread: QualifiedThreadId,
2627 runtime_instance: RuntimeInstance,
2628 ) -> Result<()> {
2629 let state = self.concurrent_state_mut()?;
2630 state.take_next_switch_item()?;
2633 let thread_data = state.get_mut(guest_thread.thread)?;
2634 let sync_call_set = thread_data.sync_call_set;
2635 if let Some(guest_id) = thread_data.instance_rep {
2636 self.instance_state(runtime_instance)
2637 .thread_handle_table()
2638 .guest_thread_remove(guest_id)?;
2639 }
2640 let state = self.concurrent_state_mut()?;
2641
2642 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2644 if let Some(Event::Subtask {
2645 status: Status::Returned | Status::ReturnCancelled,
2646 }) = waitable.common(state)?.event
2647 {
2648 waitable.delete_from(state)?;
2649 }
2650 }
2651
2652 state.delete(guest_thread.thread)?;
2653 state.delete(sync_call_set)?;
2654 let task = state.get_mut(guest_thread.task)?;
2655 task.threads.remove(&guest_thread.thread);
2656
2657 if task.threads.is_empty() && !task.returned_or_cancelled() {
2658 bail!(Trap::NoAsyncResult);
2659 }
2660
2661 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2662 task.decremented_interesting_task_count = true;
2663
2664 debug_assert!(state.interesting_tasks > 0);
2665 state.interesting_tasks -= 1;
2666 if state.interesting_tasks == 0
2667 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2668 {
2669 waker.wake();
2670 }
2671 }
2672
2673 state.decrement_task_ref_count(guest_thread.task)?;
2674
2675 Ok(())
2676 }
2677
2678 fn cancel_guest_subtask_without_lowered_parameters(
2687 &mut self,
2688 caller_instance: RuntimeInstance,
2689 guest_task: TableId<GuestTask>,
2690 ) -> Result<()> {
2691 let concurrent_state = self.concurrent_state_mut()?;
2692 let task = concurrent_state.get_mut(guest_task)?;
2693 assert!(!task.already_lowered_parameters());
2694 task.lower_params = None;
2698 task.lift_result = None;
2699 task.exited = true;
2700 let instance = task.instance;
2701
2702 assert_eq!(1, task.threads.len());
2709 let thread = *task.threads.iter().next().unwrap();
2710 let next_switch_item = concurrent_state.save_next_switch_item()?;
2711 self.cleanup_thread(
2712 QualifiedThreadId {
2713 task: guest_task,
2714 thread,
2715 },
2716 caller_instance,
2717 )?;
2718 self.concurrent_state_mut()?
2719 .restore_next_switch_item(next_switch_item)?;
2720
2721 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2723 let pending_count = pending.len();
2724 pending.retain(|thread, _| thread.task != guest_task);
2725
2726 if pending.len() == pending_count {
2727 let matches = |item: &WorkItem| match item {
2730 WorkItem::ResumeThread { thread, .. }
2731 | WorkItem::GuestCall {
2732 call: GuestCall { thread, .. },
2733 ..
2734 } => thread.task == guest_task,
2735 _ => false,
2736 };
2737
2738 let remove = |queue: &mut VecDeque<WorkItem>| {
2739 let count = queue.len();
2740 queue.retain(|item| !matches(item));
2741 count != queue.len()
2742 };
2743
2744 let state = self.concurrent_state_mut()?;
2745 let mut found = remove(&mut state.high_priority);
2746 found |= remove(&mut state.low_priority);
2747
2748 for item in [&mut state.switch_item, &mut state.next_switch_item] {
2749 if matches!(item, Some(item) if matches(item)) {
2750 found = true;
2751 *item = None;
2752 }
2753 }
2754
2755 if !found {
2756 bail!(Trap::SubtaskCancelAfterTerminal);
2757 }
2758 }
2759 Ok(())
2760 }
2761
2762 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2765 if !self.concurrency_support() {
2766 return Ok(self
2767 .current_scope_id_not_concurrent()?
2768 .map(|id| CurrentScope::Id(Scope::Id(id))));
2769 }
2770
2771 Ok(match self.current_thread()? {
2772 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2773 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2774 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2775 CurrentThread::None => return Ok(None),
2776 })
2777 }
2778
2779 pub(crate) fn queue_task(
2780 &mut self,
2781 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2782 ) -> Result<()> {
2783 self.concurrent_state_mut()?
2784 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2785 Ok(())
2786 }
2787
2788 fn any_may_not_suspend(&mut self) -> Result<bool> {
2797 Ok(self
2805 .concurrent_state_mut()?
2806 .table
2807 .get_mut()
2808 .iter_mut()
2809 .filter_map(|(_, entry)| {
2810 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2811 Some(task.instance)
2812 } else {
2813 None
2814 }
2815 })
2816 .collect::<Vec<_>>()
2817 .into_iter()
2818 .any(|instance| {
2819 self.instance_state(instance)
2820 .concurrent_state()
2821 .do_not_suspend
2822 }))
2823 }
2824}
2825
2826impl Instance {
2827 fn get_event(
2830 self,
2831 store: &mut StoreOpaque,
2832 guest_task: TableId<GuestTask>,
2833 set: Option<TableId<WaitableSet>>,
2834 cancellable: bool,
2835 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2836 let state = store.concurrent_state_mut()?;
2837
2838 let task = state.get_mut(guest_task)?;
2839 let event = &mut task.event;
2840 if let Some(ev) = event
2841 && (cancellable || !matches!(ev, Event::Cancelled))
2842 {
2843 log::trace!("deliver event {ev:?} to {guest_task:?}");
2844
2845 if matches!(ev, Event::Cancelled) {
2846 task.cancel_request_delivered = true;
2847 }
2848
2849 let ev = *ev;
2850 *event = None;
2851 return Ok(Some((ev, None)));
2852 }
2853
2854 let set = match set {
2855 Some(set) => set,
2856 None => return Ok(None),
2857 };
2858 let waitable = match state.get_mut(set)?.ready.pop_first() {
2859 Some(v) => v,
2860 None => return Ok(None),
2861 };
2862
2863 let common = waitable.common(state)?;
2864 let handle = match common.handle {
2865 Some(h) => h,
2866 None => bail_bug!("handle not set when delivering event"),
2867 };
2868 let event = match common.event.take() {
2869 Some(e) => e,
2870 None => bail_bug!("event not set when delivering event"),
2871 };
2872
2873 log::trace!(
2874 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2875 );
2876
2877 waitable.on_delivery(store, self, event)?;
2878
2879 Ok(Some((event, Some((waitable, handle)))))
2880 }
2881
2882 fn handle_callback_code(
2889 self,
2890 store: &mut dyn VMStore,
2891 guest_thread: QualifiedThreadId,
2892 runtime_instance: RuntimeInstance,
2893 call: impl FnOnce(&mut dyn VMStore) -> Result<u32>,
2894 ) -> Result<()> {
2895 let old_thread = store.set_thread(guest_thread)?;
2896 log::trace!(
2897 "callback-lifted call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2898 );
2899
2900 store.enter_instance(runtime_instance);
2901
2902 let mut code = call(store)?;
2903
2904 if matches!(
2910 unpack_callback_code(code).0,
2911 callback_code::YIELD | callback_code::WAIT
2912 ) && let Some((event, _)) = self.get_event(store, guest_thread.task, None, true)?
2913 {
2914 let (unpacked, set) = unpack_callback_code(code);
2915 log::trace!(
2916 "{guest_thread:?} returned callback code {unpacked} (set: {set}) with a pending \
2917 cancellation; delivering it via callback"
2918 );
2919 code = invoke_callback(store, guest_thread.task, event, 0)?;
2920 }
2921
2922 store.exit_instance(runtime_instance)?;
2923
2924 store.set_thread(old_thread)?;
2925 log::trace!("callback-lifted call: restored {old_thread:?} as current thread");
2926
2927 let store = store.store_opaque_mut();
2928 let (code, set) = unpack_callback_code(code);
2929
2930 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2931
2932 let state = store.concurrent_state_mut()?;
2933
2934 state.take_next_switch_item()?;
2935
2936 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2937 let set = store
2938 .instance_state(runtime_instance)
2939 .handle_table()
2940 .waitable_set_rep(handle)?;
2941
2942 Ok(TableId::<WaitableSet>::new(set))
2943 };
2944
2945 match code {
2946 callback_code::EXIT => {
2947 log::trace!("implicit thread {guest_thread:?} completed");
2948 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2949 task.exited = true;
2950 task.callback = None;
2951
2952 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2957
2958 store.cleanup_thread(guest_thread, runtime_instance)?;
2959 }
2960 callback_code::YIELD => {
2961 let old = state
2964 .get_mut(guest_thread.thread)?
2965 .wake_on_cancel
2966 .replace(WakeOnCancel::Yielding);
2967 if !old.is_none() {
2968 bail_bug!("thread unexpectedly had wake_on_cancel set");
2969 }
2970
2971 let call = GuestCall {
2978 thread: guest_thread,
2979 kind: GuestCallKind::DeliverEvent {
2980 instance: self,
2981 set: None,
2982 },
2983 };
2984 state.push_low_priority(WorkItem::GuestCall {
2987 instance: runtime_instance,
2988 call,
2989 });
2990 }
2991 callback_code::WAIT => {
2992 let set = get_set(store, set)?;
2993 self.wait_with_callback(store, guest_thread, set)?;
2994 }
2995 _ => bail!(Trap::UnsupportedCallbackCode),
2996 }
2997
2998 Ok(())
2999 }
3000
3001 fn wait_with_callback(
3007 self,
3008 store: &mut StoreOpaque,
3009 guest_thread: QualifiedThreadId,
3010 set: TableId<WaitableSet>,
3011 ) -> Result<()> {
3012 let state = store.concurrent_state_mut()?;
3013 state.get_mut(set)?.num_waiting += 1;
3016
3017 if state.get_mut(guest_thread.task)?.event.is_some()
3018 || !state.get_mut(set)?.ready.is_empty()
3019 {
3020 let instance = state.get_mut(guest_thread.task)?.instance;
3022 state.push_high_priority(WorkItem::GuestCall {
3023 instance,
3024 call: GuestCall {
3025 thread: guest_thread,
3026 kind: GuestCallKind::DeliverEvent {
3027 instance: self,
3028 set: Some(set),
3029 },
3030 },
3031 });
3032 return Ok(());
3033 }
3034
3035 let instance = store
3041 .concurrent_state_mut()?
3042 .get_mut(guest_thread.task)?
3043 .instance;
3044 store.switch_or_trap_if_may_not_suspend(instance)?;
3045
3046 let state = store.concurrent_state_mut()?;
3052 let old = state
3053 .get_mut(guest_thread.thread)?
3054 .wake_on_cancel
3055 .replace(WakeOnCancel::Waiting(set));
3056 if !old.is_none() {
3057 bail_bug!("thread unexpectedly had wake_on_cancel set");
3058 }
3059 let old = state
3060 .get_mut(set)?
3061 .waiting
3062 .insert(guest_thread, WaitMode::Callback(self));
3063 if !old.is_none() {
3064 bail_bug!("set's waiting set already had this thread registered");
3065 }
3066 Ok(())
3067 }
3068
3069 unsafe fn stage_call<T: 'static>(
3076 self,
3077 mut store: StoreContextMut<T>,
3078 guest_thread: QualifiedThreadId,
3079 callee: SendSyncPtr<VMFuncRef>,
3080 param_count: usize,
3081 result_count: usize,
3082 async_: bool,
3083 callback: Option<SendSyncPtr<VMFuncRef>>,
3084 post_return: Option<SendSyncPtr<VMFuncRef>>,
3085 host_caller: bool,
3086 ) -> Result<()> {
3087 unsafe fn make_call<T: 'static>(
3102 store: StoreContextMut<T>,
3103 guest_thread: QualifiedThreadId,
3104 callee: SendSyncPtr<VMFuncRef>,
3105 param_count: usize,
3106 result_count: usize,
3107 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
3108 + Send
3109 + Sync
3110 + 'static
3111 + use<T> {
3112 let token = StoreToken::new(store);
3113 move |store: &mut dyn VMStore| {
3114 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
3115
3116 store
3117 .concurrent_state_mut()?
3118 .set_thread_running(guest_thread.thread)?;
3119 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
3120 let lower = match task.lower_params.take() {
3121 Some(l) => l,
3122 None => bail_bug!("lower_params missing"),
3123 };
3124
3125 lower(store, &mut storage[..param_count])?;
3126
3127 let mut store = token.as_context_mut(store);
3128
3129 unsafe {
3132 crate::Func::call_unchecked_raw(
3133 &mut store,
3134 callee.as_non_null(),
3135 NonNull::new(
3136 &mut storage[..param_count.max(result_count)]
3137 as *mut [MaybeUninit<ValRaw>] as _,
3138 )
3139 .unwrap(),
3140 UncaughtException::Trap,
3141 )?;
3142 }
3143
3144 Ok(storage)
3145 }
3146 }
3147
3148 let call = unsafe {
3152 make_call(
3153 store.as_context_mut(),
3154 guest_thread,
3155 callee,
3156 param_count,
3157 result_count,
3158 )
3159 };
3160
3161 let callee_instance = store
3162 .0
3163 .concurrent_state_mut()?
3164 .get_mut(guest_thread.task)?
3165 .instance;
3166
3167 let fun = if callback.is_some() {
3168 assert!(async_);
3169
3170 Box::new(move |store: &mut dyn VMStore| {
3171 self.add_guest_thread_to_instance_table(
3172 guest_thread.thread,
3173 store,
3174 callee_instance.index,
3175 )?;
3176 self.handle_callback_code(store, guest_thread, callee_instance, |store| {
3177 let storage = call(store)?;
3178
3179 Ok(unsafe { storage[0].assume_init() }.get_i32() as u32)
3182 })
3183 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
3184 } else {
3185 let token = StoreToken::new(store.as_context_mut());
3186 Box::new(move |store: &mut dyn VMStore| {
3187 self.add_guest_thread_to_instance_table(
3188 guest_thread.thread,
3189 store,
3190 callee_instance.index,
3191 )?;
3192 let old_thread = store.set_thread(guest_thread)?;
3193 log::trace!(
3194 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
3195 );
3196 let flags = self.id().get(store).instance_flags(callee_instance.index);
3197
3198 let callee_async_typed = store
3199 .concurrent_state_mut()?
3200 .get_mut(guest_thread.task)?
3201 .async_typed;
3202
3203 if !async_ && callee_async_typed {
3207 store.enter_instance(callee_instance);
3208 }
3209
3210 if !callee_async_typed {
3211 store.enter_sync_call(callee_instance)?;
3212 }
3213
3214 let storage = call(store)?;
3221
3222 if !callee_async_typed {
3223 store.exit_sync_call(callee_instance)?;
3224 }
3225
3226 if !async_ {
3227 if callee_async_typed {
3233 store.exit_instance(callee_instance)?;
3234 }
3235
3236 let lift = {
3237 let state = store.concurrent_state_mut()?;
3238 if !state.get_mut(guest_thread.task)?.result.is_none() {
3239 bail_bug!("task has already produced a result");
3240 }
3241
3242 match state.get_mut(guest_thread.task)?.lift_result.take() {
3243 Some(lift) => lift,
3244 None => bail_bug!("lift_result field is missing"),
3245 }
3246 };
3247
3248 let result = (lift.lift)(store, unsafe {
3251 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
3252 &storage[..result_count],
3253 )
3254 })?;
3255
3256 let post_return_arg = match result_count {
3257 0 => ValRaw::i32(0),
3258 1 => unsafe { storage[0].assume_init() },
3261 _ => unreachable!(),
3262 };
3263
3264 unsafe {
3265 call_post_return(
3266 token.as_context_mut(store),
3267 post_return.map(|v| v.as_non_null()),
3268 post_return_arg,
3269 flags,
3270 )?;
3271 }
3272
3273 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3274 }
3275
3276 store.set_thread(old_thread)?;
3277
3278 store
3279 .concurrent_state_mut()?
3280 .get_mut(guest_thread.task)?
3281 .exited = true;
3282
3283 log::trace!(
3284 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3285 );
3286
3287 if callee_async_typed {
3288 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3293 }
3294
3295 store.cleanup_thread(guest_thread, callee_instance)?;
3297 Ok(())
3298 })
3299 };
3300
3301 store.0.concurrent_state_mut()?.push_work_item(
3302 WorkItem::GuestCall {
3303 instance: callee_instance,
3304 call: GuestCall {
3305 thread: guest_thread,
3306 kind: GuestCallKind::StartImplicit(fun),
3307 },
3308 },
3309 if host_caller {
3310 Priority::High
3311 } else {
3312 Priority::Switch
3313 },
3314 )?;
3315
3316 Ok(())
3317 }
3318
3319 unsafe fn prepare_call<T: 'static>(
3332 self,
3333 mut store: StoreContextMut<T>,
3334 start: NonNull<VMFuncRef>,
3335 return_: NonNull<VMFuncRef>,
3336 caller_instance: RuntimeComponentInstanceIndex,
3337 callee_instance: RuntimeComponentInstanceIndex,
3338 task_return_type: TypeTupleIndex,
3339 callee_async_typed: bool,
3340 memory: *mut VMMemoryDefinition,
3341 string_encoding: StringEncoding,
3342 caller_info: CallerInfo,
3343 ) -> Result<()> {
3344 enum ResultInfo {
3345 Heap { results: u32 },
3346 Stack { result_count: u32 },
3347 }
3348
3349 let result_info = match &caller_info {
3350 CallerInfo::Async {
3351 has_result: true,
3352 params,
3353 } => ResultInfo::Heap {
3354 results: match params.last() {
3356 Some(r) => r.get_u32(),
3357 None => bail_bug!("retptr missing"),
3358 },
3359 },
3360 CallerInfo::Async {
3361 has_result: false, ..
3362 } => ResultInfo::Stack { result_count: 0 },
3363 CallerInfo::Sync {
3364 result_count,
3365 params,
3366 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3367 results: match params.last() {
3369 Some(r) => r.get_u32(),
3370 None => bail_bug!("arg ptr missing"),
3371 },
3372 },
3373 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3374 result_count: *result_count,
3375 },
3376 };
3377
3378 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3379
3380 let start = SendSyncPtr::new(start);
3384 let return_ = SendSyncPtr::new(return_);
3385 let token = StoreToken::new(store.as_context_mut());
3386 let old_thread = store.0.current_guest_thread()?;
3387
3388 let state = store.0.concurrent_state_mut()?;
3389
3390 debug_assert_eq!(
3391 state.get_mut(old_thread.task)?.instance,
3392 self.runtime_instance(caller_instance)
3393 );
3394
3395 let guest_thread = GuestTask::new(
3396 state,
3397 Box::new(move |store, dst| {
3398 let mut store = token.as_context_mut(store);
3399 assert!(dst.len() <= MAX_FLAT_PARAMS);
3400 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3402 let count = match caller_info {
3403 CallerInfo::Async { params, has_result } => {
3407 let params = ¶ms[..params.len() - usize::from(has_result)];
3408 for (param, src) in params.iter().zip(&mut src) {
3409 src.write(*param);
3410 }
3411 params.len()
3412 }
3413
3414 CallerInfo::Sync { params, .. } => {
3416 for (param, src) in params.iter().zip(&mut src) {
3417 src.write(*param);
3418 }
3419 params.len()
3420 }
3421 };
3422 unsafe {
3429 crate::Func::call_unchecked_raw(
3430 &mut store,
3431 start.as_non_null(),
3432 NonNull::new(
3433 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3434 )
3435 .unwrap(),
3436 UncaughtException::Trap,
3437 )?;
3438 }
3439 dst.copy_from_slice(&src[..dst.len()]);
3440 let task = store.0.current_guest_thread()?.task;
3441 let state = store.0.concurrent_state_mut()?;
3442 Waitable::Guest(task).set_event(
3443 state,
3444 Some(Event::Subtask {
3445 status: Status::Started,
3446 }),
3447 )?;
3448 Ok(())
3449 }),
3450 LiftResult {
3451 lift: Box::new(move |store, src| {
3452 let mut store = token.as_context_mut(store);
3455 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3457 my_src.push(ValRaw::u32(*results));
3458 }
3459
3460 unsafe {
3467 crate::Func::call_unchecked_raw(
3468 &mut store,
3469 return_.as_non_null(),
3470 my_src.as_mut_slice().into(),
3471 UncaughtException::Trap,
3472 )?;
3473 }
3474
3475 let thread = store.0.current_guest_thread()?;
3476 let state = store.0.concurrent_state_mut()?;
3477 if sync_caller {
3478 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3479 if let ResultInfo::Stack { result_count } = &result_info {
3480 match result_count {
3481 0 => None,
3482 1 => Some(my_src[0]),
3483 _ => unreachable!(),
3484 }
3485 } else {
3486 None
3487 },
3488 );
3489 }
3490 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3491 }),
3492 ty: task_return_type,
3493 memory: NonNull::new(memory).map(SendSyncPtr::new),
3494 string_encoding,
3495 },
3496 Caller::Guest { thread: old_thread },
3497 None,
3498 self.runtime_instance(callee_instance),
3499 callee_async_typed,
3500 false,
3503 )?;
3504
3505 store.0.set_thread(guest_thread)?;
3508 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3509
3510 Ok(())
3511 }
3512
3513 unsafe fn call_callback<T>(
3518 self,
3519 mut store: StoreContextMut<T>,
3520 function: SendSyncPtr<VMFuncRef>,
3521 event: Event,
3522 handle: u32,
3523 ) -> Result<u32> {
3524 let (ordinal, result) = event.parts();
3525 let params = &mut [
3526 ValRaw::u32(ordinal),
3527 ValRaw::u32(handle),
3528 ValRaw::u32(result),
3529 ];
3530 unsafe {
3535 crate::Func::call_unchecked_raw(
3536 &mut store,
3537 function.as_non_null(),
3538 params.as_mut_slice().into(),
3539 UncaughtException::Trap,
3540 )?;
3541 }
3542 Ok(params[0].get_u32())
3543 }
3544
3545 unsafe fn start_call<T: 'static>(
3563 self,
3564 mut store: StoreContextMut<T>,
3565 callback: *mut VMFuncRef,
3566 post_return: *mut VMFuncRef,
3567 callee: NonNull<VMFuncRef>,
3568 param_count: u32,
3569 result_count: u32,
3570 flags: u32,
3571 storage: &mut [MaybeUninit<ValRaw>],
3572 ) -> Result<()> {
3573 let token = StoreToken::new(store.as_context_mut());
3574 let async_caller = (flags & START_FLAG_ASYNC_CALLER) != 0;
3575 let guest_thread = store.0.current_guest_thread()?;
3576 let state = store.0.concurrent_state_mut()?;
3577
3578 if !state.event_loop_running {
3583 bail!(
3584 "async function started during an original synchronous \
3585 entrypoint; please use an async-style entrypoint instead"
3586 );
3587 }
3588
3589 let callee = SendSyncPtr::new(callee);
3590 let param_count = usize::try_from(param_count)?;
3591 assert!(param_count <= MAX_FLAT_PARAMS);
3592 let result_count = usize::try_from(result_count)?;
3593 assert!(result_count <= MAX_FLAT_RESULTS);
3594
3595 let task = state.get_mut(guest_thread.task)?;
3596 let callee_async_typed = task.async_typed;
3597 let callee_instance = task.instance;
3598
3599 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3600
3601 if let Some(callback) = NonNull::new(callback) {
3602 let callback = SendSyncPtr::new(callback);
3606 task.callback = Some(Box::new(move |store, event, handle| {
3607 let store = token.as_context_mut(store);
3608 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3609 }));
3610 }
3611
3612 let Caller::Guest { thread: caller } = &task.caller else {
3613 bail_bug!("start_call unexpectedly invoked for host->guest call");
3616 };
3617 let caller = *caller;
3618 let caller_instance = state.get_mut(caller.task)?.instance;
3619
3620 unsafe {
3622 self.stage_call(
3623 store.as_context_mut(),
3624 guest_thread,
3625 callee,
3626 param_count,
3627 result_count,
3628 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3629 NonNull::new(callback).map(SendSyncPtr::new),
3630 NonNull::new(post_return).map(SendSyncPtr::new),
3631 false,
3632 )?;
3633 }
3634
3635 let old_do_not_suspend = if callee_async_typed {
3636 let state = store.0.instance_state(callee_instance).concurrent_state();
3643 let old_do_not_suspend = state.do_not_suspend;
3644 state.do_not_suspend = false;
3645 Some(old_do_not_suspend)
3646 } else {
3647 None
3648 };
3649
3650 let state = store.0.concurrent_state_mut()?;
3651
3652 let guest_waitable = Waitable::Guest(guest_thread.task);
3655 let old_set = guest_waitable.common(state)?.set;
3656 let set = state.get_mut(caller.thread)?.sync_call_set;
3657 guest_waitable.join(state, Some(set))?;
3658
3659 store.0.set_thread(CurrentThread::None)?;
3660
3661 let mut yielded = false;
3677 let (status, waitable) = loop {
3678 store.0.suspend(if yielded {
3679 SuspendReason::Waiting {
3680 set,
3681 thread: caller,
3682 }
3683 } else {
3684 yielded = true;
3685 SuspendReason::YieldingToSubtask { thread: caller }
3686 })?;
3687
3688 if let Some(old_do_not_suspend) = old_do_not_suspend {
3689 store
3690 .0
3691 .instance_state(callee_instance)
3692 .concurrent_state()
3693 .do_not_suspend = old_do_not_suspend;
3694 }
3695
3696 let state = store.0.concurrent_state_mut()?;
3697
3698 log::trace!("taking event for {:?}", guest_thread.task);
3699 let event = guest_waitable.take_event(state)?;
3700 let Some(Event::Subtask { status }) = event else {
3701 bail_bug!("subtasks should only get subtask events, got {event:?}")
3702 };
3703
3704 log::trace!("status {status:?} for {:?}", guest_thread.task);
3705
3706 if status == Status::Returned {
3707 break (status, None);
3709 } else if async_caller {
3710 let (state, counter) = store.0.instance_state_and_handle_counter(caller_instance);
3714 let handle = state
3715 .handle_table()
3716 .subtask_insert_guest(counter, guest_thread.task.rep())?;
3717 store
3718 .0
3719 .concurrent_state_mut()?
3720 .get_mut(guest_thread.task)?
3721 .common
3722 .handle = Some(handle);
3723 break (status, Some(handle));
3724 } else {
3725 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3729 }
3730 };
3731
3732 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3733
3734 store.0.set_thread(caller)?;
3736
3737 store
3738 .0
3739 .concurrent_state_mut()?
3740 .set_thread_running(caller.thread)?;
3741
3742 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3743
3744 if async_caller {
3745 let Some(slot) = storage.first_mut() else {
3746 bail_bug!("no storage for async call status");
3747 };
3748 *slot = MaybeUninit::new(ValRaw::u32(status.pack(waitable)));
3749 } else {
3750 let state = store.0.concurrent_state_mut()?;
3753 let task = state.get_mut(guest_thread.task)?;
3754 if let Some(Some(result)) = task.sync_result.take()? {
3755 storage[0] = MaybeUninit::new(result);
3756 }
3757 }
3758
3759 if status == Status::Returned {
3760 store
3761 .0
3762 .concurrent_state_mut()?
3763 .decrement_task_ref_count(guest_thread.task)?;
3764 }
3765
3766 Ok(())
3767 }
3768
3769 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3785 self,
3786 mut store: StoreContextMut<'_, T>,
3787 host_task: EnteredHostTask,
3788 result_may_require_realloc: bool,
3789 future: impl Future<Output = Result<R>> + Send + 'static,
3790 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3791 + Send
3792 + 'static,
3793 ) -> Result<u32> {
3794 let token = StoreToken::new(store.as_context_mut());
3795
3796 let (join_handle, future) = JoinHandle::run(future);
3799 let mut future = Box::pin(future);
3800
3801 let poll = tls::set(store.0, || {
3806 future
3807 .as_mut()
3808 .poll(&mut Context::from_waker(&Waker::noop()))
3809 });
3810
3811 match poll {
3812 Poll::Ready(result) => {
3814 let result = result.transpose()?;
3815 let task = store.0.current_materialized_host_task()?;
3818 lower(store.as_context_mut(), result, true, task)?;
3819 return Ok(Status::Returned.pack(None));
3820 }
3821
3822 Poll::Pending => {}
3824 }
3825
3826 let Some(task) = store.0.materialize_host_task_id()? else {
3830 bail_bug!("current thread is not a host thread")
3831 };
3832 {
3833 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3834 assert!(matches!(state, HostTaskState::CalleeStarted));
3835 *state = HostTaskState::CalleeRunning(join_handle);
3836 }
3837
3838 let future = Box::pin(async move {
3846 let result = match run_with_host_task_set(task, future).await? {
3847 Some(result) => Some(result?),
3848 None => None,
3849 };
3850 let on_complete = move |store: &mut dyn VMStore| {
3851 let mut store = token.as_context_mut(store);
3855 let old = store.0.set_thread(task)?;
3856
3857 let status = if result.is_some() {
3858 Status::Returned
3859 } else {
3860 Status::ReturnCancelled
3861 };
3862
3863 lower(store.as_context_mut(), result, false, Some(task))?;
3864 let state = store.0.concurrent_state_mut()?;
3865 match &mut state.get_mut(task)?.state {
3866 pending @ HostTaskState::CalleeCancelling => {
3869 *pending = HostTaskState::CalleeDone { cancelled: true };
3870 }
3871
3872 other => *other = HostTaskState::CalleeDone { cancelled: false },
3874 }
3875 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3876
3877 store.0.set_thread(old)?;
3878 Ok(())
3879 };
3880
3881 tls::get(move |store| {
3882 if result_may_require_realloc {
3883 store
3888 .concurrent_state_mut()?
3889 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3890 on_complete,
3891 ))));
3892 Ok(())
3893 } else {
3894 on_complete(store)
3897 }
3898 })
3899 });
3900
3901 let caller = match host_task {
3904 Some(caller) => caller,
3905 None => bail_bug!("host task wasn't created but should have been"),
3906 };
3907 let state = store.0.concurrent_state_mut()?;
3908 state.push_future(future);
3909 let instance = state.get_mut(caller.task)?.instance;
3910 let (state, counter) = store.0.instance_state_and_handle_counter(instance);
3911 let handle = state
3912 .handle_table()
3913 .subtask_insert_host(counter, task.rep())?;
3914 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3915 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3916
3917 store.0.set_thread(caller)?;
3921 Ok(Status::Started.pack(Some(handle)))
3922 }
3923
3924 pub(crate) fn task_return(
3927 self,
3928 store: &mut dyn VMStore,
3929 ty: TypeTupleIndex,
3930 options: OptionsIndex,
3931 storage: &[ValRaw],
3932 ) -> Result<()> {
3933 let guest_thread = store.current_guest_thread()?;
3934 let state = store.concurrent_state_mut()?;
3935 if !state.get_mut(guest_thread.task)?.async_lifted {
3936 bail!(Trap::TaskReturnOrCancelSyncLifted);
3937 }
3938 let lift = state
3939 .get_mut(guest_thread.task)?
3940 .lift_result
3941 .take()
3942 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3943 if !state.get_mut(guest_thread.task)?.result.is_none() {
3944 bail_bug!("task result unexpectedly already set");
3945 }
3946
3947 let CanonicalOptions {
3948 string_encoding,
3949 data_model,
3950 ..
3951 } = &self.id().get(store).component().env_component().options[options];
3952
3953 let invalid = ty != lift.ty
3954 || string_encoding != &lift.string_encoding
3955 || match data_model {
3956 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3957 Some(memory) => {
3958 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3959 let actual = self.id().get(store).runtime_memory(memory);
3960 expected != actual.as_ptr()
3961 }
3962 None => false,
3965 },
3966 CanonicalOptionsDataModel::Gc { .. } => true,
3968 };
3969
3970 if invalid {
3971 bail!(Trap::TaskReturnInvalid);
3972 }
3973
3974 log::trace!("task.return for {guest_thread:?}");
3975
3976 let result = (lift.lift)(store, storage)?;
3977 self.task_complete(store, guest_thread.task, result, Status::Returned)
3978 }
3979
3980 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3982 let guest_thread = store.current_guest_thread()?;
3983 let state = store.concurrent_state_mut()?;
3984 let task = state.get_mut(guest_thread.task)?;
3985 if !task.async_lifted {
3986 bail!(Trap::TaskReturnOrCancelSyncLifted);
3987 }
3988 if !task.cancel_request_delivered {
3989 bail!(Trap::TaskCancelNotCancelled);
3990 }
3991 _ = task
3992 .lift_result
3993 .take()
3994 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3995
3996 if !task.result.is_none() {
3997 bail_bug!("task result should not bet set yet");
3998 }
3999
4000 log::trace!("task.cancel for {guest_thread:?}");
4001
4002 self.task_complete(
4003 store,
4004 guest_thread.task,
4005 Box::new(DummyResult),
4006 Status::ReturnCancelled,
4007 )
4008 }
4009
4010 fn task_complete(
4016 self,
4017 store: &mut StoreOpaque,
4018 guest_task: TableId<GuestTask>,
4019 result: Box<dyn Any + Send + Sync>,
4020 status: Status,
4021 ) -> Result<()> {
4022 store
4023 .component_resource_tables(Some(self))?
4024 .validate_scope_exit()?;
4025
4026 let state = store.concurrent_state_mut()?;
4027 let task = state.get_mut(guest_task)?;
4028
4029 task.event = None;
4033
4034 if let Caller::Host { tx, .. } = &mut task.caller {
4035 if let Some(tx) = tx.take() {
4036 _ = tx.send(result);
4037 }
4038 } else {
4039 task.result = Some(result);
4040 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
4041 }
4042
4043 Ok(())
4044 }
4045
4046 pub(crate) fn waitable_set_new(
4048 self,
4049 store: &mut StoreOpaque,
4050 caller_instance: RuntimeComponentInstanceIndex,
4051 ) -> Result<u32> {
4052 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
4053 let (state, counter) =
4054 store.instance_state_and_handle_counter(self.runtime_instance(caller_instance));
4055 let handle = state
4056 .handle_table()
4057 .waitable_set_insert(counter, set.rep())?;
4058 log::trace!("new waitable set {set:?} (handle {handle})");
4059 Ok(handle)
4060 }
4061
4062 pub(crate) fn waitable_set_drop(
4064 self,
4065 store: &mut StoreOpaque,
4066 caller_instance: RuntimeComponentInstanceIndex,
4067 set: u32,
4068 ) -> Result<()> {
4069 let rep = store
4070 .instance_state(self.runtime_instance(caller_instance))
4071 .handle_table()
4072 .waitable_set_remove(set)?;
4073
4074 log::trace!("drop waitable set {rep} (handle {set})");
4075
4076 let set = store
4080 .concurrent_state_mut()?
4081 .get_mut(TableId::<WaitableSet>::new(rep))?;
4082 if set.num_waiting > 0 {
4083 bail!(Trap::WaitableSetDropHasWaiters);
4084 }
4085
4086 store
4087 .concurrent_state_mut()?
4088 .delete(TableId::<WaitableSet>::new(rep))?;
4089
4090 Ok(())
4091 }
4092
4093 pub(crate) fn waitable_join(
4095 self,
4096 store: &mut StoreOpaque,
4097 caller_instance: RuntimeComponentInstanceIndex,
4098 waitable_handle: u32,
4099 set_handle: u32,
4100 ) -> Result<()> {
4101 let mut instance = self.id().get_mut(store);
4102 let waitable =
4103 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
4104
4105 let set = if set_handle == 0 {
4106 None
4107 } else {
4108 let set = instance.instance_states().0[caller_instance]
4109 .handle_table()
4110 .waitable_set_rep(set_handle)?;
4111
4112 Some(TableId::<WaitableSet>::new(set))
4113 };
4114
4115 waitable.trap_if_has_sync_waiter(store.concurrent_state_mut()?)?;
4116
4117 log::trace!(
4118 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
4119 );
4120
4121 waitable.join(store.concurrent_state_mut()?, set)
4122 }
4123
4124 pub(crate) fn subtask_drop(
4126 self,
4127 store: &mut StoreOpaque,
4128 caller_instance: RuntimeComponentInstanceIndex,
4129 task_id: u32,
4130 ) -> Result<()> {
4131 self.waitable_join(store, caller_instance, task_id, 0)?;
4132
4133 let (rep, is_host) = store
4134 .instance_state(self.runtime_instance(caller_instance))
4135 .handle_table()
4136 .subtask_remove(task_id)?;
4137
4138 let concurrent_state = store.concurrent_state_mut()?;
4139 let waitable = if is_host {
4140 let id = TableId::<HostTask>::new(rep);
4141 let task = concurrent_state.get_mut(id)?;
4142 match &task.state {
4143 HostTaskState::CalleeRunning(_) | HostTaskState::CalleeCancelling => {
4144 bail!(Trap::SubtaskDropNotResolved)
4145 }
4146 HostTaskState::CalleeDone { .. } => {}
4147 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4148 bail_bug!("invalid state for callee in `subtask.drop`")
4149 }
4150 }
4151
4152 Waitable::Host(id)
4153 } else {
4154 let id = TableId::<GuestTask>::new(rep);
4155 let task = concurrent_state.get_mut(id)?;
4156 if task.lift_result.is_some() {
4157 bail!(Trap::SubtaskDropNotResolved);
4158 }
4159 Waitable::Guest(id)
4160 };
4161
4162 waitable.common(concurrent_state)?.handle = None;
4163
4164 if waitable.take_event(concurrent_state)?.is_some() {
4167 bail!(Trap::SubtaskDropNotResolved);
4168 }
4169
4170 waitable.delete_from(concurrent_state)?;
4171
4172 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
4173 Ok(())
4174 }
4175
4176 pub(crate) fn waitable_set_wait(
4178 self,
4179 store: &mut StoreOpaque,
4180 options: OptionsIndex,
4181 set: u32,
4182 payload: u32,
4183 ) -> Result<u32> {
4184 let &CanonicalOptions {
4185 instance: caller_instance,
4186 ..
4187 } = &self.id().get(store).component().env_component().options[options];
4188 let caller = self.runtime_instance(caller_instance);
4189 let rep = store
4190 .instance_state(self.runtime_instance(caller_instance))
4191 .handle_table()
4192 .waitable_set_rep(set)?;
4193
4194 self.waitable_check(
4195 store,
4196 caller,
4197 WaitableCheck::Wait,
4198 WaitableCheckParams {
4199 set: TableId::new(rep),
4200 options,
4201 payload,
4202 },
4203 )
4204 }
4205
4206 pub(crate) fn waitable_set_poll(
4208 self,
4209 store: &mut StoreOpaque,
4210 options: OptionsIndex,
4211 set: u32,
4212 payload: u32,
4213 ) -> Result<u32> {
4214 let &CanonicalOptions {
4215 instance: caller_instance,
4216 ..
4217 } = &self.id().get(store).component().env_component().options[options];
4218 let caller = self.runtime_instance(caller_instance);
4219 let rep = store
4220 .instance_state(caller)
4221 .handle_table()
4222 .waitable_set_rep(set)?;
4223
4224 self.waitable_check(
4225 store,
4226 caller,
4227 WaitableCheck::Poll,
4228 WaitableCheckParams {
4229 set: TableId::new(rep),
4230 options,
4231 payload,
4232 },
4233 )
4234 }
4235
4236 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
4238 let thread_id = store.current_guest_thread()?.thread;
4239 match store
4240 .concurrent_state_mut()?
4241 .get_mut(thread_id)?
4242 .instance_rep
4243 {
4244 Some(r) => Ok(r),
4245 None => bail_bug!("thread should have instance_rep by now"),
4246 }
4247 }
4248
4249 pub(crate) fn thread_new_indirect<T: 'static>(
4251 self,
4252 mut store: StoreContextMut<T>,
4253 runtime_instance: RuntimeComponentInstanceIndex,
4254 start_func_ty: ModuleInternedTypeIndex,
4255 start_func_table_idx: RuntimeTableIndex,
4256 start_func_idx: u32,
4257 context: i32,
4258 ) -> Result<u32> {
4259 log::trace!("creating new thread");
4260
4261 let (instance, registry) = self.id().get_mut_and_registry(store.0);
4262 let Some(start_func_ty) = instance.component().signatures().shared_type(start_func_ty)
4263 else {
4264 bail_bug!("thread.new-indirect start function type should be registered");
4265 };
4266 let callee = instance
4267 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
4268 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
4269 if callee.type_index(store.0) != start_func_ty {
4270 bail!(Trap::ThreadNewIndirectInvalidType);
4271 }
4272
4273 let token = StoreToken::new(store.as_context_mut());
4274 let start_func = Box::new(
4275 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
4276 let old_thread = store.set_thread(guest_thread)?;
4277 log::trace!(
4278 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
4279 );
4280
4281 let mut store = token.as_context_mut(store);
4282 let mut params = [ValRaw::i32(context)];
4283 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
4286
4287 store.0.set_thread(old_thread)?;
4288
4289 let runtime_instance = self.runtime_instance(runtime_instance);
4290
4291 store
4294 .0
4295 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4296
4297 store.0.cleanup_thread(guest_thread, runtime_instance)?;
4298
4299 log::trace!("explicit thread {guest_thread:?} completed");
4300 let state = store.0.concurrent_state_mut()?;
4301 if let Some(t) = old_thread.guest() {
4302 state.set_thread_running(t.thread)?;
4303 }
4304 log::trace!("thread start: restored {old_thread:?} as current thread");
4305
4306 Ok(())
4307 },
4308 );
4309
4310 let current_thread = store.0.current_guest_thread()?;
4311 let state = store.0.concurrent_state_mut()?;
4312 let parent_task = current_thread.task;
4313
4314 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4315 let thread_id = state.push(new_thread)?;
4316 state.get_mut(parent_task)?.threads.insert(thread_id);
4317
4318 log::trace!("new thread with id {thread_id:?} created");
4319
4320 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4321 }
4322
4323 pub(crate) fn resume_thread(
4324 self,
4325 store: &mut StoreOpaque,
4326 runtime_instance: RuntimeComponentInstanceIndex,
4327 thread_idx: u32,
4328 how: ResumeThread,
4329 ) -> Result<bool> {
4330 let thread_id =
4331 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4332 let state = store.concurrent_state_mut()?;
4333 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4334
4335 if store.current_guest_thread()? == guest_thread {
4336 bail!(Trap::CannotResumeThread);
4337 }
4338
4339 let priority = match how {
4340 ResumeThread::Resume => Priority::Switch,
4341 ResumeThread::ResumeLater => Priority::Low,
4342
4343 ResumeThread::Promote => {
4346 let instance = self.runtime_instance(runtime_instance);
4347 let do_not_enter = store
4348 .instance_state(instance)
4349 .concurrent_state()
4350 .do_not_enter;
4351 return store.concurrent_state_mut()?.promote_work_item_matching(
4352 |item: &WorkItem| match item {
4353 WorkItem::ResumeThread { thread, .. }
4354 | WorkItem::ResumeFiber { thread, .. } => *thread == guest_thread,
4355
4356 WorkItem::GuestCall {
4357 call: GuestCall { thread, kind },
4358 ..
4359 } => {
4360 *thread == guest_thread
4361 && match kind {
4362 GuestCallKind::DeliverEvent { .. } => !do_not_enter,
4366
4367 GuestCallKind::StartExplicit(_) => true,
4370
4371 GuestCallKind::StartImplicit(_) => false,
4375 }
4376 }
4377
4378 WorkItem::PushFuture(_) | WorkItem::WorkerFunction(_) => false,
4379 },
4380 );
4381 }
4382 };
4383
4384 let state = store.concurrent_state_mut()?;
4385 let thread = state.get_mut(guest_thread.thread)?;
4386
4387 match &thread.state {
4390 GuestThreadState::NotStartedExplicit(_) => {}
4391 GuestThreadState::Suspended(_) => {
4392 log::trace!("resuming thread {thread_id:?} that was suspended");
4393 state.schedule_suspended_thread(
4394 guest_thread,
4395 self.runtime_instance(runtime_instance),
4396 priority,
4397 )?;
4398 return Ok(true);
4399 }
4400 _ => bail!(Trap::CannotResumeThread),
4401 }
4402 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4403 GuestThreadState::NotStartedExplicit(start_func) => {
4404 log::trace!("starting thread {guest_thread:?}");
4405 let guest_call = WorkItem::GuestCall {
4406 instance: self.runtime_instance(runtime_instance),
4407 call: GuestCall {
4408 thread: guest_thread,
4409 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4410 start_func(store, guest_thread)
4411 })),
4412 },
4413 };
4414 state.push_work_item(guest_call, priority)?;
4415 }
4416 other => {
4417 thread.state = other;
4418 bail_bug!("thread state checked to be resumable above");
4419 }
4420 }
4421 Ok(true)
4422 }
4423
4424 fn add_guest_thread_to_instance_table(
4425 self,
4426 thread_id: TableId<GuestThread>,
4427 store: &mut StoreOpaque,
4428 runtime_instance: RuntimeComponentInstanceIndex,
4429 ) -> Result<u32> {
4430 let (state, counter) =
4431 store.instance_state_and_handle_counter(self.runtime_instance(runtime_instance));
4432 let guest_id = state
4433 .thread_handle_table()
4434 .guest_thread_insert(counter, thread_id.rep())?;
4435 store
4436 .concurrent_state_mut()?
4437 .get_mut(thread_id)?
4438 .instance_rep = Some(guest_id);
4439 Ok(guest_id)
4440 }
4441
4442 pub(crate) fn suspension_intrinsic(
4446 self,
4447 store: &mut StoreOpaque,
4448 caller: RuntimeComponentInstanceIndex,
4449 yielding: bool,
4450 to_thread: SuspensionTarget,
4451 ) -> Result<WaitResult> {
4452 let check_suspend = match to_thread {
4453 SuspensionTarget::Promote(thread) => {
4454 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4455 }
4456 SuspensionTarget::Resume(thread) => {
4457 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4458 bail_bug!(
4459 "`resume_thread` should only ever return false \
4460 when `ResumeThread::Promote` is passed to it"
4461 );
4462 }
4463 false
4464 }
4465 SuspensionTarget::None => true,
4466 };
4467
4468 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4469 return if yielding {
4470 Ok(WaitResult::Completed)
4471 } else {
4472 Err(Trap::CannotBlockSyncTask.into())
4473 };
4474 }
4475
4476 let guest_thread = store.current_guest_thread()?;
4477
4478 let reason = if yielding {
4479 SuspendReason::Yielding {
4480 thread: guest_thread,
4481 }
4482 } else {
4483 SuspendReason::ExplicitlySuspending {
4484 thread: guest_thread,
4485 }
4486 };
4487
4488 store.suspend(reason)?;
4489
4490 Ok(WaitResult::Completed)
4491 }
4492
4493 fn waitable_check(
4495 self,
4496 store: &mut StoreOpaque,
4497 caller: RuntimeInstance,
4498 check: WaitableCheck,
4499 params: WaitableCheckParams,
4500 ) -> Result<u32> {
4501 let guest_thread = store.current_guest_thread()?;
4502
4503 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4504
4505 match &check {
4508 WaitableCheck::Wait => {
4509 let set = params.set;
4510
4511 loop {
4516 let state = store.concurrent_state_mut()?;
4517 let task = state.get_mut(guest_thread.task)?;
4518 if !(task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4519 || !state.get_mut(set)?.ready.is_empty()
4520 {
4521 break;
4522 }
4523
4524 store.switch_or_trap_if_may_not_suspend(caller)?;
4525
4526 store.suspend(SuspendReason::Waiting {
4527 set,
4528 thread: guest_thread,
4529 })?;
4530 }
4531 }
4532 WaitableCheck::Poll => {}
4533 }
4534
4535 log::trace!(
4536 "waitable check for {guest_thread:?}; set {:?}, part two",
4537 params.set
4538 );
4539
4540 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4542
4543 let (ordinal, handle, result) = match &check {
4544 WaitableCheck::Wait => {
4545 let (event, waitable) = match event {
4546 Some(p) => p,
4547 None => bail_bug!("event expected to be present"),
4548 };
4549 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4550 let (ordinal, result) = event.parts();
4551 (ordinal, handle, result)
4552 }
4553 WaitableCheck::Poll => {
4554 if let Some((event, waitable)) = event {
4555 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4556 let (ordinal, result) = event.parts();
4557 (ordinal, handle, result)
4558 } else {
4559 log::trace!(
4560 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4561 guest_thread.task,
4562 params.set
4563 );
4564 let (ordinal, result) = Event::None.parts();
4565 (ordinal, 0, result)
4566 }
4567 }
4568 };
4569 let memory = self.options_memory_mut(store, params.options);
4570 let ptr = crate::component::func::validate_inbounds_dynamic(
4571 &CanonicalAbiInfo::POINTER_PAIR,
4572 memory,
4573 &ValRaw::u32(params.payload),
4574 )?;
4575 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4576 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4577 Ok(ordinal)
4578 }
4579
4580 pub(crate) fn subtask_cancel(
4582 self,
4583 store: &mut StoreOpaque,
4584 caller_instance: RuntimeComponentInstanceIndex,
4585 async_: bool,
4586 task_id: u32,
4587 ) -> Result<u32> {
4588 let (rep, is_host) = store
4589 .instance_state(self.runtime_instance(caller_instance))
4590 .handle_table()
4591 .subtask_rep(task_id)?;
4592 let waitable = if is_host {
4593 Waitable::Host(TableId::<HostTask>::new(rep))
4594 } else {
4595 Waitable::Guest(TableId::<GuestTask>::new(rep))
4596 };
4597 let concurrent_state = store.concurrent_state_mut()?;
4598
4599 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4600
4601 waitable.trap_if_in_waitable_set(concurrent_state)?;
4602
4603 let needs_block;
4604 if let Waitable::Host(host_task) = waitable {
4605 let state = &mut concurrent_state.get_mut(host_task)?.state;
4606 match state {
4607 HostTaskState::CalleeRunning(handle) => {
4614 handle.abort();
4615 *state = HostTaskState::CalleeCancelling;
4616 needs_block = true;
4617 }
4618
4619 HostTaskState::CalleeCancelling | HostTaskState::CalleeDone { cancelled: true } => {
4622 bail!(Trap::SubtaskCancelAfterTerminal);
4623 }
4624 HostTaskState::CalleeDone { cancelled: false } => {
4625 *state = HostTaskState::CalleeDone { cancelled: true };
4628 needs_block = false;
4629 }
4630
4631 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4634 bail_bug!("invalid states for host callee")
4635 }
4636 }
4637 } else {
4638 let guest_task = TableId::<GuestTask>::new(rep);
4639 let task = concurrent_state.get_mut(guest_task)?;
4640
4641 if task.cancel_requested {
4644 bail!(Trap::SubtaskCancelAfterTerminal);
4645 }
4646
4647 if !task.already_lowered_parameters() {
4648 store.cancel_guest_subtask_without_lowered_parameters(
4649 self.runtime_instance(caller_instance),
4650 guest_task,
4651 )?;
4652 return Ok(Status::StartCancelled as u32);
4653 } else if !task.returned_or_cancelled() {
4654 task.cancel_requested = true;
4661 task.event = Some(Event::Cancelled);
4662 let runtime_instance = task.instance;
4663 for thread in task.threads.clone() {
4664 let thread = QualifiedThreadId {
4665 task: guest_task,
4666 thread,
4667 };
4668 let concurrent_state = store.concurrent_state_mut()?;
4669 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4670
4671 let yield_ = |store: &mut StoreOpaque| {
4672 let state = store.instance_state(runtime_instance).concurrent_state();
4677 let old_do_not_suspend = state.do_not_suspend;
4678 state.do_not_suspend = false;
4679
4680 let caller = store.current_guest_thread()?;
4681
4682 let state = store.concurrent_state_mut()?;
4687 let set = state.get_mut(caller.thread)?.sync_call_set;
4688 waitable.join(state, Some(set))?;
4689
4690 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4691
4692 let state = store.concurrent_state_mut()?;
4693 waitable.join(state, None)?;
4694
4695 store
4696 .instance_state(runtime_instance)
4697 .concurrent_state()
4698 .do_not_suspend = old_do_not_suspend;
4699
4700 Ok::<(), crate::Error>(())
4701 };
4702
4703 match thread_mut.wake_on_cancel.take() {
4704 WakeOnCancel::Waiting(set) => {
4705 let instance = concurrent_state.take_callback_waiter(set, thread)?;
4707 let item = WorkItem::GuestCall {
4708 instance: runtime_instance,
4709 call: GuestCall {
4710 thread,
4711 kind: GuestCallKind::DeliverEvent {
4712 instance,
4713 set: Some(set),
4714 },
4715 },
4716 };
4717 concurrent_state.set_switch_item(item)?;
4718
4719 yield_(store)?;
4720
4721 break;
4722 }
4723 WakeOnCancel::Yielding => {
4724 if concurrent_state.promote_thread_work_item(thread)? {
4725 yield_(store)?;
4726 break;
4727 } else if store
4728 .instance_state(runtime_instance)
4729 .concurrent_state()
4730 .pending
4731 .contains_key(&thread)
4732 {
4733 store
4743 .concurrent_state_mut()?
4744 .get_mut(thread.thread)?
4745 .wake_on_cancel = WakeOnCancel::Yielding;
4746 } else {
4747 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4748 }
4749 }
4750 WakeOnCancel::None => {}
4751 }
4752 }
4753
4754 needs_block = !store
4757 .concurrent_state_mut()?
4758 .get_mut(guest_task)?
4759 .returned_or_cancelled()
4760 } else {
4761 needs_block = false;
4762 }
4763 };
4764
4765 if needs_block {
4769 if async_ {
4770 return Ok(BLOCKED);
4771 }
4772
4773 let old_next_switch_item = if is_host {
4783 Some(store.concurrent_state_mut()?.save_next_switch_item()?)
4784 } else {
4785 None
4786 };
4787
4788 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4791
4792 if let Some(item) = old_next_switch_item {
4793 let state = store.concurrent_state_mut()?;
4794 state.restore_next_switch_item(item)?;
4795 }
4796
4797 }
4799
4800 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4801 if let Some(Event::Subtask {
4802 status: status @ (Status::Returned | Status::ReturnCancelled),
4803 }) = event
4804 {
4805 Ok(status as u32)
4806 } else {
4807 bail!(Trap::SubtaskCancelAfterTerminal);
4808 }
4809 }
4810}
4811
4812pub trait VMComponentAsyncStore {
4820 unsafe fn prepare_call(
4826 &mut self,
4827 instance: Instance,
4828 memory: *mut VMMemoryDefinition,
4829 start: NonNull<VMFuncRef>,
4830 return_: NonNull<VMFuncRef>,
4831 caller_instance: RuntimeComponentInstanceIndex,
4832 callee_instance: RuntimeComponentInstanceIndex,
4833 task_return_type: TypeTupleIndex,
4834 callee_async: bool,
4835 string_encoding: StringEncoding,
4836 result_count: u32,
4837 storage: *mut ValRaw,
4838 storage_len: usize,
4839 ) -> Result<()>;
4840
4841 unsafe fn start_call(
4844 &mut self,
4845 instance: Instance,
4846 callback: *mut VMFuncRef,
4847 post_return: *mut VMFuncRef,
4848 callee: NonNull<VMFuncRef>,
4849 param_count: u32,
4850 result_count: u32,
4851 flags: u32,
4852 storage: *mut MaybeUninit<ValRaw>,
4853 storage_len: usize,
4854 ) -> Result<()>;
4855
4856 fn future_write(
4858 &mut self,
4859 instance: Instance,
4860 caller: RuntimeComponentInstanceIndex,
4861 ty: TypeFutureTableIndex,
4862 options: OptionsIndex,
4863 future: u32,
4864 address: u32,
4865 ) -> Result<u32>;
4866
4867 fn future_read(
4869 &mut self,
4870 instance: Instance,
4871 caller: RuntimeComponentInstanceIndex,
4872 ty: TypeFutureTableIndex,
4873 options: OptionsIndex,
4874 future: u32,
4875 address: u32,
4876 ) -> Result<u32>;
4877
4878 fn future_drop_writable(
4880 &mut self,
4881 instance: Instance,
4882 ty: TypeFutureTableIndex,
4883 writer: u32,
4884 ) -> Result<()>;
4885
4886 fn stream_write(
4888 &mut self,
4889 instance: Instance,
4890 caller: RuntimeComponentInstanceIndex,
4891 ty: TypeStreamTableIndex,
4892 options: OptionsIndex,
4893 stream: u32,
4894 address: u32,
4895 count: u32,
4896 ) -> Result<u32>;
4897
4898 fn stream_read(
4900 &mut self,
4901 instance: Instance,
4902 caller: RuntimeComponentInstanceIndex,
4903 ty: TypeStreamTableIndex,
4904 options: OptionsIndex,
4905 stream: u32,
4906 address: u32,
4907 count: u32,
4908 ) -> Result<u32>;
4909
4910 fn flat_stream_write(
4913 &mut self,
4914 instance: Instance,
4915 caller: RuntimeComponentInstanceIndex,
4916 ty: TypeStreamTableIndex,
4917 options: OptionsIndex,
4918 payload_size: u32,
4919 payload_align: u32,
4920 stream: u32,
4921 address: u32,
4922 count: u32,
4923 ) -> Result<u32>;
4924
4925 fn flat_stream_read(
4928 &mut self,
4929 instance: Instance,
4930 caller: RuntimeComponentInstanceIndex,
4931 ty: TypeStreamTableIndex,
4932 options: OptionsIndex,
4933 payload_size: u32,
4934 payload_align: u32,
4935 stream: u32,
4936 address: u32,
4937 count: u32,
4938 ) -> Result<u32>;
4939
4940 fn stream_drop_writable(
4942 &mut self,
4943 instance: Instance,
4944 ty: TypeStreamTableIndex,
4945 writer: u32,
4946 ) -> Result<()>;
4947
4948 fn error_context_debug_message(
4950 &mut self,
4951 instance: Instance,
4952 ty: TypeComponentLocalErrorContextTableIndex,
4953 options: OptionsIndex,
4954 err_ctx_handle: u32,
4955 debug_msg_address: u32,
4956 ) -> Result<()>;
4957
4958 fn thread_new_indirect(
4960 &mut self,
4961 instance: Instance,
4962 caller: RuntimeComponentInstanceIndex,
4963 func_ty_idx: ModuleInternedTypeIndex,
4964 start_func_table_idx: RuntimeTableIndex,
4965 start_func_idx: u32,
4966 context: i32,
4967 ) -> Result<u32>;
4968}
4969
4970impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4972 unsafe fn prepare_call(
4973 &mut self,
4974 instance: Instance,
4975 memory: *mut VMMemoryDefinition,
4976 start: NonNull<VMFuncRef>,
4977 return_: NonNull<VMFuncRef>,
4978 caller_instance: RuntimeComponentInstanceIndex,
4979 callee_instance: RuntimeComponentInstanceIndex,
4980 task_return_type: TypeTupleIndex,
4981 callee_async: bool,
4982 string_encoding: StringEncoding,
4983 result_count_or_max_if_async: u32,
4984 storage: *mut ValRaw,
4985 storage_len: usize,
4986 ) -> Result<()> {
4987 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4991
4992 unsafe {
4993 instance.prepare_call(
4994 StoreContextMut(self),
4995 start,
4996 return_,
4997 caller_instance,
4998 callee_instance,
4999 task_return_type,
5000 callee_async,
5001 memory,
5002 string_encoding,
5003 match result_count_or_max_if_async {
5004 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
5005 params,
5006 has_result: false,
5007 },
5008 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
5009 params,
5010 has_result: true,
5011 },
5012 result_count => CallerInfo::Sync {
5013 params,
5014 result_count,
5015 },
5016 },
5017 )
5018 }
5019 }
5020
5021 unsafe fn start_call(
5022 &mut self,
5023 instance: Instance,
5024 callback: *mut VMFuncRef,
5025 post_return: *mut VMFuncRef,
5026 callee: NonNull<VMFuncRef>,
5027 param_count: u32,
5028 result_count: u32,
5029 flags: u32,
5030 storage: *mut MaybeUninit<ValRaw>,
5031 storage_len: usize,
5032 ) -> Result<()> {
5033 unsafe {
5034 instance.start_call(
5035 StoreContextMut(self),
5036 callback,
5037 post_return,
5038 callee,
5039 param_count,
5040 result_count,
5041 flags,
5042 core::slice::from_raw_parts_mut(storage, storage_len),
5046 )
5047 }
5048 }
5049
5050 fn future_write(
5051 &mut self,
5052 instance: Instance,
5053 caller: RuntimeComponentInstanceIndex,
5054 ty: TypeFutureTableIndex,
5055 options: OptionsIndex,
5056 future: u32,
5057 address: u32,
5058 ) -> Result<u32> {
5059 instance
5060 .guest_write(
5061 StoreContextMut(self),
5062 caller,
5063 TransmitIndex::Future(ty),
5064 options,
5065 None,
5066 future,
5067 address,
5068 1,
5069 )
5070 .map(|result| result.encode())
5071 }
5072
5073 fn future_read(
5074 &mut self,
5075 instance: Instance,
5076 caller: RuntimeComponentInstanceIndex,
5077 ty: TypeFutureTableIndex,
5078 options: OptionsIndex,
5079 future: u32,
5080 address: u32,
5081 ) -> Result<u32> {
5082 instance
5083 .guest_read(
5084 StoreContextMut(self),
5085 caller,
5086 TransmitIndex::Future(ty),
5087 options,
5088 None,
5089 future,
5090 address,
5091 1,
5092 )
5093 .map(|result| result.encode())
5094 }
5095
5096 fn stream_write(
5097 &mut self,
5098 instance: Instance,
5099 caller: RuntimeComponentInstanceIndex,
5100 ty: TypeStreamTableIndex,
5101 options: OptionsIndex,
5102 stream: u32,
5103 address: u32,
5104 count: u32,
5105 ) -> Result<u32> {
5106 instance
5107 .guest_write(
5108 StoreContextMut(self),
5109 caller,
5110 TransmitIndex::Stream(ty),
5111 options,
5112 None,
5113 stream,
5114 address,
5115 count,
5116 )
5117 .map(|result| result.encode())
5118 }
5119
5120 fn stream_read(
5121 &mut self,
5122 instance: Instance,
5123 caller: RuntimeComponentInstanceIndex,
5124 ty: TypeStreamTableIndex,
5125 options: OptionsIndex,
5126 stream: u32,
5127 address: u32,
5128 count: u32,
5129 ) -> Result<u32> {
5130 instance
5131 .guest_read(
5132 StoreContextMut(self),
5133 caller,
5134 TransmitIndex::Stream(ty),
5135 options,
5136 None,
5137 stream,
5138 address,
5139 count,
5140 )
5141 .map(|result| result.encode())
5142 }
5143
5144 fn future_drop_writable(
5145 &mut self,
5146 instance: Instance,
5147 ty: TypeFutureTableIndex,
5148 writer: u32,
5149 ) -> Result<()> {
5150 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
5151 }
5152
5153 fn flat_stream_write(
5154 &mut self,
5155 instance: Instance,
5156 caller: RuntimeComponentInstanceIndex,
5157 ty: TypeStreamTableIndex,
5158 options: OptionsIndex,
5159 payload_size: u32,
5160 payload_align: u32,
5161 stream: u32,
5162 address: u32,
5163 count: u32,
5164 ) -> Result<u32> {
5165 instance
5166 .guest_write(
5167 StoreContextMut(self),
5168 caller,
5169 TransmitIndex::Stream(ty),
5170 options,
5171 Some(FlatAbi {
5172 size: payload_size,
5173 align: payload_align,
5174 }),
5175 stream,
5176 address,
5177 count,
5178 )
5179 .map(|result| result.encode())
5180 }
5181
5182 fn flat_stream_read(
5183 &mut self,
5184 instance: Instance,
5185 caller: RuntimeComponentInstanceIndex,
5186 ty: TypeStreamTableIndex,
5187 options: OptionsIndex,
5188 payload_size: u32,
5189 payload_align: u32,
5190 stream: u32,
5191 address: u32,
5192 count: u32,
5193 ) -> Result<u32> {
5194 instance
5195 .guest_read(
5196 StoreContextMut(self),
5197 caller,
5198 TransmitIndex::Stream(ty),
5199 options,
5200 Some(FlatAbi {
5201 size: payload_size,
5202 align: payload_align,
5203 }),
5204 stream,
5205 address,
5206 count,
5207 )
5208 .map(|result| result.encode())
5209 }
5210
5211 fn stream_drop_writable(
5212 &mut self,
5213 instance: Instance,
5214 ty: TypeStreamTableIndex,
5215 writer: u32,
5216 ) -> Result<()> {
5217 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
5218 }
5219
5220 fn error_context_debug_message(
5221 &mut self,
5222 instance: Instance,
5223 ty: TypeComponentLocalErrorContextTableIndex,
5224 options: OptionsIndex,
5225 err_ctx_handle: u32,
5226 debug_msg_address: u32,
5227 ) -> Result<()> {
5228 instance.error_context_debug_message(
5229 StoreContextMut(self),
5230 ty,
5231 options,
5232 err_ctx_handle,
5233 debug_msg_address,
5234 )
5235 }
5236
5237 fn thread_new_indirect(
5238 &mut self,
5239 instance: Instance,
5240 caller: RuntimeComponentInstanceIndex,
5241 func_ty_idx: ModuleInternedTypeIndex,
5242 start_func_table_idx: RuntimeTableIndex,
5243 start_func_idx: u32,
5244 context: i32,
5245 ) -> Result<u32> {
5246 instance.thread_new_indirect(
5247 StoreContextMut(self),
5248 caller,
5249 func_ty_idx,
5250 start_func_table_idx,
5251 start_func_idx,
5252 context,
5253 )
5254 }
5255}
5256
5257type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
5258
5259async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
5262where
5263 F: Future,
5264{
5265 let mut future = pin!(future);
5266 future::poll_fn(|cx| {
5267 let old_thread = match tls::get(|store| store.set_thread(task)) {
5268 Ok(thread) => thread,
5269 Err(error) => return Poll::Ready(Err(error)),
5270 };
5271 let result = future.as_mut().poll(cx);
5272 match tls::get(|store| store.set_thread(old_thread)) {
5273 Ok(_) => result.map(Ok),
5274 Err(error) => Poll::Ready(Err(error)),
5275 }
5276 })
5277 .await
5278}
5279
5280pub(crate) struct HostTask {
5284 common: WaitableCommon,
5285
5286 call_context: CallContext,
5289
5290 state: HostTaskState,
5291
5292 group: TaskGroupId,
5293}
5294
5295enum HostTaskState {
5296 CalleeStarted,
5301
5302 CalleeRunning(JoinHandle),
5307
5308 CalleeCancelling,
5312
5313 CalleeFinished(LiftedResult),
5317
5318 CalleeDone { cancelled: bool },
5321}
5322
5323impl HostTask {
5324 fn new(
5325 concurrent_state: &mut ConcurrentState,
5326 state: HostTaskState,
5327 caller: QualifiedThreadId,
5328 ) -> Result<Self> {
5329 let group = concurrent_state.get_mut(caller.task)?.group;
5330 concurrent_state.increment_group_ref_count(group)?;
5331
5332 Ok(Self {
5333 common: WaitableCommon::default(),
5334 call_context: CallContext::default(),
5335 state,
5336 group,
5337 })
5338 }
5339}
5340
5341impl TableDebug for HostTask {
5342 fn type_name() -> &'static str {
5343 "HostTask"
5344 }
5345}
5346
5347type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5348
5349enum Caller {
5351 Host {
5353 tx: Option<oneshot::Sender<LiftedResult>>,
5355 caller: Option<TableId<HostTask>>,
5359 },
5360 Guest {
5362 thread: QualifiedThreadId,
5364 },
5365}
5366
5367struct LiftResult {
5370 lift: RawLift,
5371 ty: TypeTupleIndex,
5372 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5373 string_encoding: StringEncoding,
5374}
5375
5376#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5381pub(crate) struct QualifiedThreadId {
5382 task: TableId<GuestTask>,
5383 thread: TableId<GuestThread>,
5384}
5385
5386impl QualifiedThreadId {
5387 fn qualify(
5388 state: &mut ConcurrentState,
5389 thread: TableId<GuestThread>,
5390 ) -> Result<QualifiedThreadId> {
5391 Ok(QualifiedThreadId {
5392 task: state.get_mut(thread)?.parent_task,
5393 thread,
5394 })
5395 }
5396}
5397
5398impl fmt::Debug for QualifiedThreadId {
5399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5400 f.debug_tuple("QualifiedThreadId")
5401 .field(&self.task.rep())
5402 .field(&self.thread.rep())
5403 .finish()
5404 }
5405}
5406
5407enum GuestThreadState {
5408 NotStartedImplicit,
5409 NotStartedExplicit(
5410 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5411 ),
5412 Running,
5413 Suspended(StoreFiber<'static>),
5414 Ready {
5415 fiber: StoreFiber<'static>,
5416 },
5417 Completed,
5418}
5419
5420impl GuestThreadState {
5421 fn check_no_fiber(&self) -> Result<()> {
5422 match self {
5423 Self::Suspended(_) | Self::Ready { .. } => bail_bug!("thread already owns a fiber"),
5424 Self::NotStartedImplicit
5425 | Self::NotStartedExplicit(_)
5426 | Self::Running
5427 | Self::Completed => Ok(()),
5428 }
5429 }
5430}
5431
5432impl fmt::Debug for GuestThreadState {
5433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5434 match self {
5435 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5436 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5437 Self::Running => f.debug_tuple("Running").finish(),
5438 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5439 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5440 Self::Completed => f.debug_tuple("Completed").finish(),
5441 }
5442 }
5443}
5444
5445#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5446enum WakeOnCancel {
5447 None,
5448 Waiting(TableId<WaitableSet>),
5449 Yielding,
5450}
5451
5452impl WakeOnCancel {
5453 fn is_none(self) -> bool {
5454 matches!(self, WakeOnCancel::None)
5455 }
5456
5457 fn replace(&mut self, other: WakeOnCancel) -> Self {
5458 let old = *self;
5459 *self = other;
5460 old
5461 }
5462
5463 fn take(&mut self) -> Self {
5464 self.replace(WakeOnCancel::None)
5465 }
5466}
5467
5468pub struct GuestThread {
5469 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5472 parent_task: TableId<GuestTask>,
5474 wake_on_cancel: WakeOnCancel,
5477 state: GuestThreadState,
5479 instance_rep: Option<u32>,
5482 sync_call_set: TableId<WaitableSet>,
5484 old_do_not_suspend: Option<bool>,
5487}
5488
5489impl GuestThread {
5490 fn from_instance(
5493 state: Pin<&mut ComponentInstance>,
5494 caller_instance: RuntimeComponentInstanceIndex,
5495 guest_thread: u32,
5496 ) -> Result<TableId<Self>> {
5497 let rep = state.instance_states().0[caller_instance]
5498 .thread_handle_table()
5499 .guest_thread_rep(guest_thread)?;
5500 Ok(TableId::new(rep))
5501 }
5502
5503 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5504 let sync_call_set = state.push(WaitableSet {
5505 is_sync_call_set: true,
5506 ..WaitableSet::default()
5507 })?;
5508 Ok(Self {
5509 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5510 parent_task,
5511 wake_on_cancel: WakeOnCancel::None,
5512 state: GuestThreadState::NotStartedImplicit,
5513 instance_rep: None,
5514 sync_call_set,
5515 old_do_not_suspend: None,
5516 })
5517 }
5518
5519 fn new_explicit(
5520 state: &mut ConcurrentState,
5521 parent_task: TableId<GuestTask>,
5522 start_func: Box<
5523 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5524 >,
5525 ) -> Result<Self> {
5526 state.increment_task_ref_count(parent_task)?;
5527 let sync_call_set = state.push(WaitableSet {
5528 is_sync_call_set: true,
5529 ..WaitableSet::default()
5530 })?;
5531 Ok(Self {
5532 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5533 parent_task,
5534 wake_on_cancel: WakeOnCancel::None,
5535 state: GuestThreadState::NotStartedExplicit(start_func),
5536 instance_rep: None,
5537 sync_call_set,
5538 old_do_not_suspend: None,
5539 })
5540 }
5541}
5542
5543impl TableDebug for GuestThread {
5544 fn type_name() -> &'static str {
5545 "GuestThread"
5546 }
5547}
5548
5549enum SyncResult {
5550 NotProduced,
5551 Produced(Option<ValRaw>),
5552 Taken,
5553}
5554
5555impl SyncResult {
5556 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5557 Ok(match mem::replace(self, SyncResult::Taken) {
5558 SyncResult::NotProduced => None,
5559 SyncResult::Produced(val) => Some(val),
5560 SyncResult::Taken => {
5561 bail_bug!("attempted to take a synchronous result that was already taken")
5562 }
5563 })
5564 }
5565}
5566
5567pub(crate) struct GuestTask {
5569 common: WaitableCommon,
5571 lower_params: Option<RawLower>,
5573 lift_result: Option<LiftResult>,
5575 result: Option<LiftedResult>,
5578 callback: Option<CallbackFn>,
5581 caller: Caller,
5583 call_context: CallContext,
5588 sync_result: SyncResult,
5591 cancel_request_delivered: bool,
5595 cancel_requested: bool,
5600 starting_sent: bool,
5603 instance: RuntimeInstance,
5610 event: Option<Event>,
5612 exited: bool,
5614 threads: HashSet<TableId<GuestThread>>,
5616 async_typed: bool,
5619 async_lifted: bool,
5622 decremented_interesting_task_count: bool,
5625 group: TaskGroupId,
5627 ref_count: usize,
5632}
5633
5634impl GuestTask {
5635 fn already_lowered_parameters(&self) -> bool {
5636 self.lower_params.is_none()
5638 }
5639
5640 fn returned_or_cancelled(&self) -> bool {
5641 self.lift_result.is_none()
5643 }
5644
5645 fn new(
5646 state: &mut ConcurrentState,
5647 lower_params: RawLower,
5648 lift_result: LiftResult,
5649 caller: Caller,
5650 callback: Option<CallbackFn>,
5651 instance: RuntimeInstance,
5652 async_typed: bool,
5653 async_lifted: bool,
5654 ) -> Result<QualifiedThreadId> {
5655 let group = match caller {
5656 Caller::Guest { thread } => {
5657 let group = state.get_mut(thread.task)?.group;
5658 state.increment_group_ref_count(group)?;
5659 group
5660 }
5661 Caller::Host { .. } => state.make_task_group()?,
5662 };
5663
5664 let task = state.push(Self {
5665 common: WaitableCommon::default(),
5666 lower_params: Some(lower_params),
5667 lift_result: Some(lift_result),
5668 result: None,
5669 callback,
5670 caller,
5671 call_context: CallContext::default(),
5672 sync_result: SyncResult::NotProduced,
5673 cancel_request_delivered: false,
5674 cancel_requested: false,
5675 starting_sent: false,
5676 instance,
5677 event: None,
5678 exited: false,
5679 threads: HashSet::new(),
5680 async_typed,
5681 async_lifted,
5682 decremented_interesting_task_count: false,
5683 group,
5684 ref_count: 2,
5687 })?;
5688 let new_thread = GuestThread::new_implicit(state, task)?;
5689 let thread = state.push(new_thread)?;
5690 state.get_mut(task)?.threads.insert(thread);
5691 state.interesting_tasks += 1;
5692 let thread = QualifiedThreadId { task, thread };
5693 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5694 Ok(thread)
5695 }
5696}
5697
5698impl TableDebug for GuestTask {
5699 fn type_name() -> &'static str {
5700 "GuestTask"
5701 }
5702}
5703
5704#[derive(Default)]
5706struct WaitableCommon {
5707 event: Option<Event>,
5709 set: Option<TableId<WaitableSet>>,
5711 handle: Option<u32>,
5713}
5714
5715#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5717enum Waitable {
5718 Host(TableId<HostTask>),
5720 Guest(TableId<GuestTask>),
5722 Transmit(TableId<TransmitHandle>),
5724}
5725
5726impl Waitable {
5727 fn from_instance(
5730 state: Pin<&mut ComponentInstance>,
5731 caller_instance: RuntimeComponentInstanceIndex,
5732 waitable: u32,
5733 ) -> Result<Self> {
5734 use crate::runtime::vm::component::Waitable;
5735
5736 let (waitable, kind) = state.instance_states().0[caller_instance]
5737 .handle_table()
5738 .waitable_rep(waitable)?;
5739
5740 Ok(match kind {
5741 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5742 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5743 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5744 })
5745 }
5746
5747 fn rep(&self) -> u32 {
5749 match self {
5750 Self::Host(id) => id.rep(),
5751 Self::Guest(id) => id.rep(),
5752 Self::Transmit(id) => id.rep(),
5753 }
5754 }
5755
5756 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5760 log::trace!("waitable {self:?} join set {set:?}");
5761
5762 let old = mem::replace(&mut self.common(state)?.set, set);
5763
5764 if let Some(old) = old {
5765 match *self {
5766 Waitable::Host(id) => state.remove_child(id, old),
5767 Waitable::Guest(id) => state.remove_child(id, old),
5768 Waitable::Transmit(id) => state.remove_child(id, old),
5769 }?;
5770
5771 state.get_mut(old)?.ready.remove(self);
5772 }
5773
5774 if let Some(set) = set {
5775 match *self {
5776 Waitable::Host(id) => state.add_child(id, set),
5777 Waitable::Guest(id) => state.add_child(id, set),
5778 Waitable::Transmit(id) => state.add_child(id, set),
5779 }?;
5780
5781 if self.common(state)?.event.is_some() {
5782 self.mark_ready(state)?;
5783 }
5784 }
5785
5786 Ok(())
5787 }
5788
5789 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5791 Ok(match self {
5792 Self::Host(id) => &mut state.get_mut(*id)?.common,
5793 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5794 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5795 })
5796 }
5797
5798 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5804 if self.common(state)?.set.is_some() {
5805 bail!(Trap::WaitableSyncAndAsync);
5806 }
5807 Ok(())
5808 }
5809
5810 fn trap_if_has_sync_waiter(&self, state: &mut ConcurrentState) -> Result<()> {
5816 if let Some(set) = self.common(state)?.set
5817 && state.get_mut(set)?.is_sync_call_set
5818 {
5819 bail!(Trap::WaitableSyncAndAsync);
5820 }
5821 Ok(())
5822 }
5823
5824 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5828 log::trace!("set event for {self:?}: {event:?}");
5829 self.common(state)?.event = event;
5830 self.mark_ready(state)
5831 }
5832
5833 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5835 let common = self.common(state)?;
5836 let event = common.event.take();
5837 if let Some(set) = self.common(state)?.set {
5838 state.get_mut(set)?.ready.remove(self);
5839 }
5840
5841 Ok(event)
5842 }
5843
5844 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5848 if let Some(set) = self.common(state)?.set {
5849 state.get_mut(set)?.ready.insert(*self);
5850 state.wake_waiter(set)?;
5851 }
5852 Ok(())
5853 }
5854
5855 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5858 match self {
5859 Self::Host(task) => state.delete_host_task(*task),
5860 Self::Guest(task) => state.decrement_task_ref_count(*task),
5861 Self::Transmit(task) => Ok(state.delete(*task).map(drop)?),
5862 }
5863 }
5864}
5865
5866impl fmt::Debug for Waitable {
5867 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5868 match self {
5869 Self::Host(id) => write!(f, "{id:?}"),
5870 Self::Guest(id) => write!(f, "{id:?}"),
5871 Self::Transmit(id) => write!(f, "{id:?}"),
5872 }
5873 }
5874}
5875
5876#[derive(Default)]
5878struct WaitableSet {
5879 ready: BTreeSet<Waitable>,
5881 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5883 num_waiting: usize,
5886 is_sync_call_set: bool,
5889}
5890
5891impl WaitableSet {
5892 fn stop_waiting(&mut self) -> Result<()> {
5894 self.num_waiting = match self.num_waiting.checked_sub(1) {
5895 Some(n) => n,
5896 None => bail_bug!("waiter not accounted for in waitable set"),
5897 };
5898 Ok(())
5899 }
5900}
5901
5902impl TableDebug for WaitableSet {
5903 fn type_name() -> &'static str {
5904 "WaitableSet"
5905 }
5906}
5907
5908type RawLower =
5910 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5911
5912type RawLift = Box<
5914 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5915>;
5916
5917type LiftedResult = Box<dyn Any + Send + Sync>;
5921
5922struct DummyResult;
5925
5926#[derive(Default)]
5928pub struct ConcurrentInstanceState {
5929 backpressure: u16,
5931 do_not_enter: bool,
5933 do_not_suspend: bool,
5936 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5939}
5940
5941impl ConcurrentInstanceState {
5942 pub fn pending_is_empty(&self) -> bool {
5943 self.pending.is_empty()
5944 }
5945}
5946
5947#[derive(Debug, Copy, Clone)]
5948pub(crate) enum CurrentThread {
5949 Guest(QualifiedThreadId),
5952 Host(TableId<HostTask>),
5954 DeferredHost(QualifiedThreadId),
5957 None,
5960}
5961
5962impl CurrentThread {
5963 fn guest(&self) -> Option<&QualifiedThreadId> {
5964 match self {
5965 Self::Guest(id) => Some(id),
5966 _ => None,
5967 }
5968 }
5969
5970 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5971 match self {
5972 Self::Guest(id) => Some(id.task),
5973 _ => None,
5974 }
5975 }
5976
5977 fn is_none(&self) -> bool {
5978 matches!(self, Self::None)
5979 }
5980}
5981
5982impl From<QualifiedThreadId> for CurrentThread {
5983 fn from(id: QualifiedThreadId) -> Self {
5984 Self::Guest(id)
5985 }
5986}
5987
5988impl From<TableId<HostTask>> for CurrentThread {
5989 fn from(id: TableId<HostTask>) -> Self {
5990 Self::Host(id)
5991 }
5992}
5993
5994enum Priority {
5995 Switch,
5996 High,
5997 Low,
5998}
5999
6000pub struct ConcurrentState {
6002 unforced_current_thread: CurrentThread,
6008
6009 deferred_host_call_context: Option<CallContext>,
6015
6016 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
6021 table: AlwaysMut<ResourceTable>,
6023 switch_item: Option<WorkItem>,
6031 next_switch_item: Option<WorkItem>,
6037 high_priority: VecDeque<WorkItem>,
6039 low_priority: VecDeque<WorkItem>,
6041 suspend_reason: Option<SuspendReason>,
6045 worker: Option<StoreFiber<'static>>,
6049 worker_item: Option<WorkerItem>,
6051
6052 global_error_context_ref_counts:
6065 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
6066
6067 interesting_tasks: usize,
6080
6081 interesting_tasks_empty_waker: Option<Waker>,
6085
6086 ready_for_concurrent_call_waker: Option<Waker>,
6091
6092 event_loop_running: bool,
6094
6095 saved_next_switch_items: Vec<Option<WorkItem>>,
6098
6099 #[cfg(feature = "task-group-hook")]
6101 task_group_hook: Option<Box<dyn TaskGroupHook>>,
6102}
6103
6104impl Default for ConcurrentState {
6105 fn default() -> Self {
6106 Self {
6107 unforced_current_thread: CurrentThread::None,
6108 deferred_host_call_context: None,
6109 table: AlwaysMut::new(ResourceTable::new()),
6110 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
6111 switch_item: None,
6112 next_switch_item: None,
6113 high_priority: VecDeque::new(),
6114 low_priority: VecDeque::new(),
6115 suspend_reason: None,
6116 worker: None,
6117 worker_item: None,
6118 global_error_context_ref_counts: BTreeMap::new(),
6119 interesting_tasks: 0,
6120 interesting_tasks_empty_waker: None,
6121 ready_for_concurrent_call_waker: None,
6122 event_loop_running: false,
6123 saved_next_switch_items: Vec::new(),
6124 #[cfg(feature = "task-group-hook")]
6125 task_group_hook: None,
6126 }
6127 }
6128}
6129
6130impl ConcurrentState {
6131 pub(crate) fn take_fibers_and_futures(
6148 &mut self,
6149 fibers: &mut Vec<StoreFiber<'static>>,
6150 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
6151 ) {
6152 let ConcurrentState {
6153 table,
6154 worker,
6155 switch_item,
6156 next_switch_item,
6157 high_priority,
6158 low_priority,
6159 saved_next_switch_items,
6160 futures: pending_futures,
6161 worker_item: _,
6162 unforced_current_thread: _,
6163 deferred_host_call_context: _,
6164 suspend_reason: _,
6165 global_error_context_ref_counts: _,
6166 interesting_tasks: _,
6167 interesting_tasks_empty_waker: _,
6168 ready_for_concurrent_call_waker: _,
6169 event_loop_running: _,
6170 #[cfg(feature = "task-group-hook")]
6171 task_group_hook: _,
6172 } = self;
6173
6174 let mut items = Vec::new();
6175 for (_, entry) in table.get_mut().iter_mut() {
6176 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6177 for mode in mem::take(&mut set.waiting).into_values() {
6178 match mode {
6179 WaitMode::Fiber(fiber) => {
6180 fibers.push(fiber);
6181 }
6182 WaitMode::Callback(_) => {}
6183 }
6184 }
6185 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6186 match mem::replace(&mut thread.state, GuestThreadState::Completed) {
6187 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6188 fibers.push(fiber);
6189 }
6190 GuestThreadState::NotStartedImplicit
6191 | GuestThreadState::NotStartedExplicit(_)
6192 | GuestThreadState::Running
6193 | GuestThreadState::Completed => {}
6194 }
6195 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
6196 if let Some(item) = item.take() {
6197 items.push(item);
6198 }
6199 }
6200 }
6201
6202 if let Some(fiber) = worker.take() {
6203 fibers.push(fiber);
6204 }
6205
6206 let mut handle_item = |item| match item {
6207 WorkItem::ResumeFiber { fiber, .. } => {
6208 fibers.push(fiber);
6209 }
6210 WorkItem::PushFuture(future) => {
6211 pending_futures
6212 .get_mut()
6213 .as_mut()
6214 .unwrap()
6215 .push(future.into_inner());
6216 }
6217 WorkItem::ResumeThread { .. }
6218 | WorkItem::GuestCall { .. }
6219 | WorkItem::WorkerFunction(_) => {}
6220 };
6221
6222 for item in items {
6223 handle_item(item);
6224 }
6225 if let Some(item) = switch_item.take() {
6226 handle_item(item);
6227 }
6228 if let Some(item) = next_switch_item.take() {
6229 handle_item(item);
6230 }
6231 for item in mem::take(high_priority) {
6232 handle_item(item);
6233 }
6234 for item in mem::take(low_priority) {
6235 handle_item(item);
6236 }
6237 for item in mem::take(saved_next_switch_items)
6238 .into_iter()
6239 .filter_map(|v| v)
6240 {
6241 handle_item(item);
6242 }
6243
6244 if let Some(them) = pending_futures.get_mut().take() {
6245 futures.push(them);
6246 }
6247 }
6248
6249 #[cfg(feature = "gc")]
6250 pub(crate) fn trace_fiber_roots(
6251 &mut self,
6252 modules: &ModuleRegistry,
6253 unwind: &dyn Unwind,
6254 gc_roots_list: &mut GcRootsList,
6255 ) {
6256 let ConcurrentState {
6257 table,
6258 worker,
6259 switch_item,
6260 next_switch_item,
6261 high_priority,
6262 low_priority,
6263 saved_next_switch_items,
6264
6265 futures: _,
6269
6270 worker_item: _,
6272 unforced_current_thread: _,
6273 deferred_host_call_context: _,
6274 suspend_reason: _,
6275 global_error_context_ref_counts: _,
6276 interesting_tasks: _,
6277 interesting_tasks_empty_waker: _,
6278 ready_for_concurrent_call_waker: _,
6279 event_loop_running: _,
6280 #[cfg(feature = "task-group-hook")]
6281 task_group_hook: _,
6282 } = self;
6283
6284 for (_, entry) in table.get_mut().iter_mut() {
6285 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
6286 for mode in set.waiting.values_mut() {
6287 match mode {
6288 WaitMode::Fiber(fiber) => {
6289 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6290 }
6291 WaitMode::Callback(_) => {}
6292 }
6293 }
6294 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
6295 match &mut thread.state {
6296 GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber } => {
6297 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6298 }
6299 GuestThreadState::NotStartedImplicit
6300 | GuestThreadState::NotStartedExplicit(_)
6301 | GuestThreadState::Running
6302 | GuestThreadState::Completed => {}
6303 }
6304 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
6305 entry.downcast_mut::<Option<WorkItem>>()
6306 {
6307 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6308 }
6309 }
6310
6311 if let Some(fiber) = worker {
6312 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6313 }
6314
6315 let mut handle_item = |item: &mut WorkItem| match item {
6316 WorkItem::ResumeFiber { fiber, .. } => {
6317 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
6318 }
6319 WorkItem::PushFuture(_future) => {
6320 }
6323 WorkItem::ResumeThread { .. }
6324 | WorkItem::GuestCall { .. }
6325 | WorkItem::WorkerFunction(_) => {}
6326 };
6327
6328 if let Some(item) = switch_item {
6329 handle_item(item);
6330 }
6331 if let Some(item) = next_switch_item {
6332 handle_item(item);
6333 }
6334 for item in high_priority {
6335 handle_item(item);
6336 }
6337 for item in low_priority {
6338 handle_item(item);
6339 }
6340 for item in saved_next_switch_items
6341 .iter_mut()
6342 .filter_map(|v| v.as_mut())
6343 {
6344 handle_item(item);
6345 }
6346 }
6347
6348 fn schedule_suspended_thread(
6349 &mut self,
6350 thread: QualifiedThreadId,
6351 instance: RuntimeInstance,
6352 priority: Priority,
6353 ) -> Result<()> {
6354 if matches!(priority, Priority::Switch) && self.switch_item.is_some() {
6356 bail_bug!("switch item already set");
6357 }
6358 let slot = &mut self.get_mut(thread.thread)?.state;
6359 if !matches!(slot, GuestThreadState::Suspended(_)) {
6360 bail!(Trap::CannotResumeThread);
6361 }
6362 let GuestThreadState::Suspended(fiber) = mem::replace(slot, GuestThreadState::Running)
6363 else {
6364 unreachable!()
6365 };
6366 let item = WorkItem::ResumeFiber {
6367 instance,
6368 thread,
6369 fiber,
6370 };
6371 match priority {
6372 Priority::Switch => self.switch_item = Some(item),
6373 Priority::High => self.push_high_priority(item),
6374 Priority::Low => self.push_low_priority(item),
6375 }
6376 Ok(())
6377 }
6378
6379 fn set_thread_running(&mut self, thread: TableId<GuestThread>) -> Result<()> {
6380 let slot = &mut self.get_mut(thread)?.state;
6381 slot.check_no_fiber()?;
6382 *slot = GuestThreadState::Running;
6383 Ok(())
6384 }
6385
6386 fn save_next_switch_item(&mut self) -> Result<TableId<Option<WorkItem>>> {
6387 let saved = self.push(None::<WorkItem>)?;
6390 let slot = self.table.get_mut().get_mut(&Resource::from(saved))?;
6391 *slot = self.next_switch_item.take();
6392 Ok(saved)
6393 }
6394
6395 fn restore_next_switch_item(&mut self, saved: TableId<Option<WorkItem>>) -> Result<()> {
6396 if self.next_switch_item.is_some() {
6397 bail_bug!("next switch item already set when restoring");
6398 }
6399 self.next_switch_item = self.delete(saved)?;
6400 Ok(())
6401 }
6402
6403 fn take_callback_waiter(
6404 &mut self,
6405 set: TableId<WaitableSet>,
6406 thread: QualifiedThreadId,
6407 ) -> Result<Instance> {
6408 let waiting = &mut self.get_mut(set)?.waiting;
6409 let instance = match waiting.get(&thread) {
6410 Some(WaitMode::Callback(instance)) => *instance,
6411 other => bail_bug!("expected `Some(WaitMode::Callback(_))`; got `{other:?}`"),
6412 };
6413 waiting.remove(&thread);
6414 Ok(instance)
6415 }
6416
6417 fn push<V: Send + Sync + 'static>(
6418 &mut self,
6419 value: V,
6420 ) -> Result<TableId<V>, ResourceTableError> {
6421 self.table.get_mut().push(value).map(TableId::from)
6422 }
6423
6424 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6425 self.table.get_mut().get_mut(&Resource::from(id))
6426 }
6427
6428 pub fn add_child<T: 'static, U: 'static>(
6429 &mut self,
6430 child: TableId<T>,
6431 parent: TableId<U>,
6432 ) -> Result<(), ResourceTableError> {
6433 self.table
6434 .get_mut()
6435 .add_child(Resource::from(child), Resource::from(parent))
6436 }
6437
6438 pub fn remove_child<T: 'static, U: 'static>(
6439 &mut self,
6440 child: TableId<T>,
6441 parent: TableId<U>,
6442 ) -> Result<(), ResourceTableError> {
6443 self.table
6444 .get_mut()
6445 .remove_child(Resource::from(child), Resource::from(parent))
6446 }
6447
6448 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6449 self.table.get_mut().delete(Resource::from(id))
6450 }
6451
6452 fn push_future(&mut self, future: HostTaskFuture) {
6453 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6460 }
6461
6462 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6463 if self.switch_item.is_some() {
6464 self.push_high_priority(item);
6467 bail_bug!("switch item already set");
6468 }
6469
6470 self.switch_item = Some(item);
6471 log::trace!("set switch item: {:?}", self.switch_item.as_ref().unwrap());
6472
6473 Ok(())
6474 }
6475
6476 fn take_next_switch_item(&mut self) -> Result<()> {
6477 if let Some(item) = self.next_switch_item.take() {
6478 self.set_switch_item(item)?;
6479 }
6480 Ok(())
6481 }
6482
6483 fn wake_waiter(&mut self, set: TableId<WaitableSet>) -> Result<()> {
6486 let Some((&thread, _)) = self.get_mut(set)?.waiting.first_key_value() else {
6489 return Ok(());
6490 };
6491 let wake_on_cancel = self.get_mut(thread.thread)?.wake_on_cancel;
6492 if wake_on_cancel != WakeOnCancel::None && wake_on_cancel != WakeOnCancel::Waiting(set) {
6493 bail_bug!("thread {thread:?} has unexpected wake_on_cancel value {wake_on_cancel:?}");
6494 }
6495 let instance = self.get_mut(thread.task)?.instance;
6496 self.get_mut(thread.thread)?.wake_on_cancel.take();
6497 let (_, mode) = self.get_mut(set)?.waiting.pop_first().unwrap();
6498
6499 let item = match mode {
6500 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber {
6501 instance,
6502 thread,
6503 fiber,
6504 },
6505 WaitMode::Callback(callback_instance) => WorkItem::GuestCall {
6506 instance,
6507 call: GuestCall {
6508 thread,
6509 kind: GuestCallKind::DeliverEvent {
6510 instance: callback_instance,
6511 set: Some(set),
6512 },
6513 },
6514 },
6515 };
6516 self.push_high_priority(item);
6517 Ok(())
6518 }
6519
6520 fn push_high_priority(&mut self, item: WorkItem) {
6521 self.high_priority.push_front(item);
6522 log::trace!(
6523 "push high priority: {:?}",
6524 self.high_priority.front().unwrap()
6525 );
6526 }
6527
6528 fn push_low_priority(&mut self, item: WorkItem) {
6529 self.low_priority.push_front(item);
6530 log::trace!(
6531 "push low priority: {:?}",
6532 self.low_priority.front().unwrap()
6533 );
6534 }
6535
6536 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6537 match priority {
6538 Priority::Switch => self.set_switch_item(item)?,
6539 Priority::High => self.push_high_priority(item),
6540 Priority::Low => self.push_low_priority(item),
6541 }
6542
6543 Ok(())
6544 }
6545
6546 fn promote_instance_local_thread_work_item(
6547 &mut self,
6548 current_instance: RuntimeInstance,
6549 ) -> Result<bool> {
6550 log::trace!("promote thread work items for {current_instance:?}");
6551
6552 self.promote_work_item_matching(|item: &WorkItem| {
6553 let result = match item {
6554 WorkItem::ResumeThread { instance, .. }
6555 | WorkItem::ResumeFiber { instance, .. }
6556 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6557 _ => false,
6558 };
6559
6560 log::trace!("candidate {item:?}: {result}");
6561 result
6562 })
6563 }
6564
6565 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6566 self.promote_work_item_matching(|item: &WorkItem| match item {
6567 WorkItem::ResumeThread {
6568 thread: item_thread,
6569 ..
6570 }
6571 | WorkItem::GuestCall {
6572 call:
6573 GuestCall {
6574 thread: item_thread,
6575 ..
6576 },
6577 ..
6578 } => *item_thread == thread,
6579 _ => false,
6580 })
6581 }
6582
6583 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6584 where
6585 F: FnMut(&WorkItem) -> bool,
6586 {
6587 if self.switch_item.is_none() {
6594 if let Some(index) = self.high_priority.iter().rposition(&mut predicate) {
6595 self.switch_item = self.high_priority.remove(index);
6596 }
6597 }
6598 if self.switch_item.is_none() {
6599 if let Some(index) = self.low_priority.iter().rposition(predicate) {
6600 self.switch_item = self.low_priority.remove(index);
6601 }
6602 }
6603 Ok(self.switch_item.is_some())
6604 }
6605
6606 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6609 match task {
6610 Scope::HostId(task) => {
6611 let task: TableId<HostTask> = TableId::new(task);
6612 Ok(&mut self.get_mut(task)?.call_context)
6613 }
6614 Scope::Id(task) => {
6615 let task: TableId<GuestTask> = TableId::new(task);
6616 Ok(&mut self.get_mut(task)?.call_context)
6617 }
6618 }
6619 }
6620
6621 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6622 self.deferred_host_call_context.as_mut()
6623 }
6624
6625 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6626 match self.futures.get_mut().as_mut() {
6627 Some(f) => Ok(f),
6628 None => bail_bug!("futures field of concurrent state is currently taken"),
6629 }
6630 }
6631
6632 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6633 self.table.get_mut()
6634 }
6635
6636 fn debug_assert_deferred_host_invariant(&self) {
6637 debug_assert_eq!(
6638 self.deferred_host_call_context.is_some(),
6639 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6640 "a deferred host thread and call context must exist together",
6641 );
6642 }
6643
6644 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6645 self.debug_assert_deferred_host_invariant();
6646 let caller = match self.unforced_current_thread {
6647 CurrentThread::DeferredHost(caller) => caller,
6648 thread => return Ok(thread),
6649 };
6650
6651 let task = HostTask::new(self, HostTaskState::CalleeStarted, caller)?;
6653 let task = self.push(task)?;
6654 let call_context = self
6655 .deferred_host_call_context
6656 .take()
6657 .expect("deferred host call context should be present");
6658 self.get_mut(task)
6659 .expect("newly inserted host task should be present")
6660 .call_context = call_context;
6661 self.unforced_current_thread = CurrentThread::Host(task);
6662 self.debug_assert_deferred_host_invariant();
6663 log::trace!("new host task materialized {task:?}");
6664 Ok(CurrentThread::Host(task))
6665 }
6666
6667 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6668 match self.materialize_host_task()? {
6669 CurrentThread::Host(id) => Ok(Some(id)),
6670 CurrentThread::None => Ok(None),
6671 CurrentThread::Guest(_) => {
6672 bail_bug!("tried to materialize a host task id from a guest thread")
6673 }
6674 CurrentThread::DeferredHost(_) => {
6675 bail_bug!(
6676 "current thread is a deferred host thread which should have been materialized"
6677 )
6678 }
6679 }
6680 }
6681
6682 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6683 match self.materialize_host_task()? {
6684 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6685 _ => bail_bug!("current scope is not a deferred host scope"),
6686 }
6687 }
6688
6689 fn increment_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6690 let count = &mut self.get_mut(task)?.ref_count;
6691 *count += 1;
6692 log::trace!("increment {task:?} to {count}");
6693 Ok(())
6694 }
6695
6696 fn decrement_task_ref_count(&mut self, task: TableId<GuestTask>) -> Result<()> {
6697 let count = &mut self.get_mut(task)?.ref_count;
6698 if *count == 0 {
6699 bail_bug!("can't decrement task ref count that's already zero");
6700 }
6701 *count -= 1;
6702 log::trace!("decrement {task:?} to {count}");
6703 if *count == 0 {
6704 let task = self.delete(task)?;
6705
6706 self.decrement_group_ref_count(task.group)?;
6707
6708 debug_assert!(task.decremented_interesting_task_count);
6715 }
6716 Ok(())
6717 }
6718
6719 fn delete_host_task(&mut self, task: TableId<HostTask>) -> Result<()> {
6720 log::trace!("delete host task {task:?}");
6721 let task = self.delete(task)?;
6722
6723 self.decrement_group_ref_count(task.group)
6724 }
6725}
6726
6727fn for_any_lower<
6730 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6731>(
6732 fun: F,
6733) -> F {
6734 fun
6735}
6736
6737fn for_any_lift<
6739 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6740>(
6741 fun: F,
6742) -> F {
6743 fun
6744}
6745
6746fn check_ambient_store(id: StoreId) {
6747 let message = "\
6748 `Future`s which depend on asynchronous component tasks, streams, or \
6749 futures to complete may only be polled from the event loop of the \
6750 store to which they belong. Please use \
6751 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6752 ";
6753 tls::try_get(|store| {
6754 let matched = match store {
6755 tls::TryGet::Some(store) => store.id() == id,
6756 tls::TryGet::Taken | tls::TryGet::None => false,
6757 };
6758
6759 if !matched {
6760 panic!("{message}")
6761 }
6762 });
6763}
6764
6765fn unpack_callback_code(code: u32) -> (u32, u32) {
6766 (code & 0xF, code >> 4)
6767}
6768
6769struct WaitableCheckParams {
6773 set: TableId<WaitableSet>,
6774 options: OptionsIndex,
6775 payload: u32,
6776}
6777
6778enum WaitableCheck {
6781 Wait,
6782 Poll,
6783}
6784
6785pub(crate) struct PreparedCall<R> {
6787 handle: Func,
6789 thread: QualifiedThreadId,
6791 param_count: usize,
6793 rx: oneshot::Receiver<LiftedResult>,
6796 _phantom: PhantomData<R>,
6797}
6798
6799impl<R> PreparedCall<R> {
6800 pub(crate) fn acquire_task_id(&self, store: &mut StoreOpaque) -> Result<TaskId> {
6807 store
6808 .concurrent_state_mut()?
6809 .increment_task_ref_count(self.thread.task)?;
6810
6811 Ok(TaskId {
6812 task: self.thread.task,
6813 })
6814 }
6815}
6816
6817pub(crate) struct TaskId {
6819 task: TableId<GuestTask>,
6820}
6821
6822impl TaskId {
6823 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6835 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6836 if !task.already_lowered_parameters() {
6837 task.lower_params = Some(Box::new(|store, _| {
6842 store.set_trapped();
6843 bail!("the host future for this call was dropped before it started")
6844 }));
6845 } else {
6846 _ = store
6851 .concurrent_state_mut()?
6852 .decrement_task_ref_count(self.task);
6853 }
6854
6855 Ok(())
6856 }
6857}
6858
6859pub(crate) fn prepare_call<T, R>(
6865 mut store: StoreContextMut<T>,
6866 handle: Func,
6867 param_count: usize,
6868 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6869 + Send
6870 + Sync
6871 + 'static,
6872 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6873 + Send
6874 + Sync
6875 + 'static,
6876) -> Result<PreparedCall<R>> {
6877 if !store.0.may_enter() {
6881 bail!(Trap::CannotEnterComponent);
6882 }
6883
6884 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6885
6886 let instance = handle.instance().id().get(store.0);
6887 let options = &instance.component().env_component().options[options];
6888 let ty = &instance.component().types()[ty];
6889 let async_typed = ty.async_;
6890 let async_lifted = raw_options.async_;
6891 let task_return_type = ty.results;
6892 let component_instance = raw_options.instance;
6893 let callback = options.callback.map(|i| instance.runtime_callback(i));
6894 let memory = options
6895 .memory()
6896 .map(|i| instance.runtime_memory(i))
6897 .map(SendSyncPtr::new);
6898 let string_encoding = options.string_encoding;
6899 let token = StoreToken::new(store.as_context_mut());
6900 let caller = store.0.materialize_host_task_id()?;
6901 let state = store.0.concurrent_state_mut()?;
6902
6903 let (tx, rx) = oneshot::channel();
6904
6905 let instance = handle.instance().runtime_instance(component_instance);
6906 let thread = GuestTask::new(
6907 state,
6908 Box::new(for_any_lower(move |store, params| {
6909 lower_params(token.as_context_mut(store), params)
6910 })),
6911 LiftResult {
6912 lift: Box::new(for_any_lift(move |store, result| {
6913 lift_result(store, result)
6914 })),
6915 ty: task_return_type,
6916 memory,
6917 string_encoding,
6918 },
6919 Caller::Host {
6920 tx: Some(tx),
6921 caller,
6922 },
6923 callback.map(|callback| {
6924 let callback = SendSyncPtr::new(callback);
6925 let instance = handle.instance();
6926 Box::new(move |store: &mut dyn VMStore, event, handle| {
6927 let store = token.as_context_mut(store);
6928 unsafe { instance.call_callback(store, callback, event, handle) }
6931 }) as CallbackFn
6932 }),
6933 instance,
6934 async_typed,
6935 async_lifted,
6936 )?;
6937
6938 Ok(PreparedCall {
6939 handle,
6940 thread,
6941 param_count,
6942 rx,
6943 _phantom: PhantomData,
6944 })
6945}
6946
6947pub(crate) struct StagedCall<R> {
6948 store: StoreId,
6949 rx: oneshot::Receiver<LiftedResult>,
6950 _marker: PhantomData<fn() -> R>,
6951 group: TaskGroupId,
6959}
6960
6961impl<R> StagedCall<R> {
6962 pub(crate) fn new<T: 'static>(
6973 mut store: StoreContextMut<T>,
6974 prepared: PreparedCall<R>,
6975 ) -> Result<StagedCall<R>> {
6976 let PreparedCall {
6977 handle,
6978 thread,
6979 param_count,
6980 rx,
6981 ..
6982 } = prepared;
6983
6984 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6985
6986 let state = store.0.concurrent_state_mut()?;
6987 let group = state.get_mut(thread.task)?.group;
6988 state.decrement_task_ref_count(thread.task)?;
6989
6990 Ok(StagedCall {
6991 store: store.0.id(),
6992 rx,
6993 _marker: PhantomData,
6994 group,
6995 })
6996 }
6997}
6998
6999impl<R> Future for StagedCall<R>
7000where
7001 R: 'static,
7002{
7003 type Output = Result<R>;
7004
7005 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
7006 check_ambient_store(self.store);
7007 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
7008 Ok(r) => match r.downcast() {
7009 Ok(r) => Ok(*r),
7010 Err(_) => bail_bug!("wrong type of value produced"),
7011 },
7012 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
7013 })
7014 }
7015}
7016
7017fn stage_call0<T: 'static>(
7020 store: StoreContextMut<T>,
7021 handle: Func,
7022 guest_thread: QualifiedThreadId,
7023 param_count: usize,
7024) -> Result<()> {
7025 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
7026 let is_concurrent = raw_options.async_;
7027 let callback = raw_options.callback;
7028 let instance = handle.instance();
7029 let callee = handle.lifted_core_func(store.0);
7030 let post_return = raw_options
7031 .post_return
7032 .map(|i| instance.id().get(store.0).runtime_post_return(i));
7033 let callback = callback.map(|i| {
7034 let instance = instance.id().get(store.0);
7035 SendSyncPtr::new(instance.runtime_callback(i))
7036 });
7037
7038 log::trace!("queueing call {guest_thread:?}");
7039
7040 unsafe {
7044 instance.stage_call(
7045 store,
7046 guest_thread,
7047 SendSyncPtr::new(callee),
7048 param_count,
7049 1,
7050 is_concurrent,
7051 callback,
7052 post_return.map(SendSyncPtr::new),
7053 true,
7054 )
7055 }
7056}
7057
7058#[cfg(all(test, feature = "cranelift", feature = "wat"))]
7059mod tests {
7060 use super::*;
7061 use crate::component::{Component, Linker};
7062 use crate::store::AsStoreOpaque;
7063 use crate::{Config, Engine};
7064
7065 fn host_subtask(
7066 state: HostTaskState,
7067 event: Option<Event>,
7068 ) -> Result<(Store<()>, Instance, TableId<HostTask>, u32)> {
7069 let mut config = Config::new();
7070 config.wasm_component_model_async(true);
7071 let engine = Engine::new(&config)?;
7072 let component = Component::new(&engine, "(component)")?;
7073 let mut store = Store::new(&engine, ());
7074 let instance = Linker::new(&engine).instantiate(&mut store, &component)?;
7075 let store_opaque = store.as_store_opaque();
7076 let concurrent_state = store_opaque.concurrent_state_mut()?;
7077 let group = concurrent_state.make_task_group()?;
7080 let task = concurrent_state.push(HostTask {
7081 common: WaitableCommon::default(),
7082 call_context: CallContext::default(),
7083 state,
7084 group,
7085 })?;
7086 let (state, counter) = store_opaque.instance_state_and_handle_counter(
7087 instance.runtime_instance(RuntimeComponentInstanceIndex::from_u32(0)),
7088 );
7089 let handle = state
7090 .handle_table()
7091 .subtask_insert_host(counter, task.rep())?;
7092 let common = &mut store_opaque.concurrent_state_mut()?.get_mut(task)?.common;
7093 common.handle = Some(handle);
7094 common.event = event;
7095 Ok((store, instance, task, handle))
7096 }
7097
7098 #[test]
7099 fn host_subtask_drop_during_cancellation() -> Result<()> {
7100 for abort_completed in [false, true] {
7101 let (handle, future) = JoinHandle::run(future::pending::<()>());
7102 let mut future = pin!(future);
7103 let (mut store, instance, task, handle) =
7104 host_subtask(HostTaskState::CalleeRunning(handle), None)?;
7105 let store = store.as_store_opaque();
7106 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7107 assert_eq!(
7108 instance.subtask_cancel(store, caller, true, handle)?,
7109 BLOCKED
7110 );
7111 if abort_completed {
7112 assert!(matches!(
7115 future
7116 .as_mut()
7117 .poll(&mut Context::from_waker(Waker::noop())),
7118 Poll::Ready(None),
7119 ));
7120 }
7121 for async_ in [false, true] {
7122 let err = instance
7123 .subtask_cancel(store, caller, async_, handle)
7124 .unwrap_err();
7125 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7126 }
7127 let err = instance.subtask_drop(store, caller, handle).unwrap_err();
7128 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7129 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7130 }
7131 Ok(())
7132 }
7133
7134 #[test]
7135 fn host_subtask_cancel_after_completion() -> Result<()> {
7136 for async_ in [false, true] {
7137 let (mut store, instance, task, handle) = host_subtask(
7138 HostTaskState::CalleeDone { cancelled: false },
7139 Some(Event::Subtask {
7140 status: Status::Returned,
7141 }),
7142 )?;
7143 let store = store.as_store_opaque();
7144 let caller = RuntimeComponentInstanceIndex::from_u32(0);
7145 assert_eq!(
7146 instance.subtask_cancel(store, caller, async_, handle)?,
7147 Status::Returned as u32,
7148 );
7149 let err = instance
7150 .subtask_cancel(store, caller, async_, handle)
7151 .unwrap_err();
7152 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskCancelAfterTerminal);
7153 instance.subtask_drop(store, caller, handle)?;
7154 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7155 }
7156 Ok(())
7157 }
7158
7159 #[test]
7160 fn host_subtask_drop_requires_terminal_event_delivery() -> Result<()> {
7161 for (cancelled, status) in [
7162 (false, Status::Returned),
7163 (true, Status::Returned),
7164 (true, Status::ReturnCancelled),
7165 ] {
7166 for delivered in [false, true] {
7167 let event = if delivered {
7168 None
7169 } else {
7170 Some(Event::Subtask { status })
7171 };
7172 let (mut store, instance, task, handle) =
7173 host_subtask(HostTaskState::CalleeDone { cancelled }, event)?;
7174 let store = store.as_store_opaque();
7175 let result = instance.subtask_drop(
7176 store,
7177 RuntimeComponentInstanceIndex::from_u32(0),
7178 handle,
7179 );
7180 if delivered {
7181 result?;
7182 assert!(store.concurrent_state_mut()?.get_mut(task).is_err());
7183 } else {
7184 let err = result.unwrap_err();
7185 assert_eq!(err.downcast::<Trap>()?, Trap::SubtaskDropNotResolved);
7186 assert!(store.concurrent_state_mut()?.get_mut(task).is_ok());
7187 }
7188 }
7189 }
7190 Ok(())
7191 }
7192}