1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::{
56 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
57};
58use crate::fiber::{self, StoreFiber, StoreFiberYield};
59use crate::hash_set::HashSet;
60#[cfg(feature = "gc")]
61use crate::module::ModuleRegistry;
62use crate::prelude::*;
63use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
64#[cfg(feature = "gc")]
65use crate::vm::GcRootsList;
66use crate::vm::component::{CallContext, ComponentInstance, CurrentScope, InstanceState, Scope};
67use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore};
68use crate::{
69 AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, bail,
70};
71use crate::{Instance as ModuleInstance, bail_bug};
72use alloc::borrow::ToOwned;
73use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
74use core::any::Any;
75use core::cell::UnsafeCell;
76use core::fmt;
77use core::future;
78use core::future::Future;
79use core::marker::PhantomData;
80use core::mem::{self, ManuallyDrop, MaybeUninit};
81use core::ops::DerefMut;
82use core::pin::{Pin, pin};
83use core::ptr::{self, NonNull};
84use core::task::{Context, Poll, Waker};
85use futures::channel::oneshot;
86use futures::stream::{FuturesUnordered, StreamExt};
87use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
88use table::{TableDebug, TableId};
89use wasmtime_environ::component::{
90 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
91 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
92 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
93 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
94 TypeFuncIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
95};
96use wasmtime_environ::packed_option::ReservedValue;
97use wasmtime_environ::{NUM_COMPONENT_CONTEXT_SLOTS, Trap};
98#[cfg(feature = "gc")]
99use wasmtime_unwinder::Unwind;
100
101pub use abort::JoinHandle;
102pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
103pub use future_stream_any::{FutureAny, StreamAny};
104pub use futures_and_streams::{
105 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
106 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
107 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
108};
109pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
110
111mod abort;
112mod error_contexts;
113mod func;
114mod future_stream_any;
115mod futures_and_streams;
116pub(crate) mod table;
117pub(crate) mod tls;
118
119const BLOCKED: u32 = 0xffff_ffff;
122
123#[derive(Clone, Copy, Eq, PartialEq, Debug)]
125pub enum Status {
126 Starting = 0,
127 Started = 1,
128 Returned = 2,
129 StartCancelled = 3,
130 ReturnCancelled = 4,
131}
132
133impl Status {
134 pub fn pack(self, waitable: Option<u32>) -> u32 {
140 assert!(matches!(self, Status::Returned) == waitable.is_none());
141 let waitable = waitable.unwrap_or(0);
142 assert!(waitable < (1 << 28));
143 (waitable << 4) | (self as u32)
144 }
145}
146
147#[derive(Clone, Copy, Debug)]
150enum Event {
151 None,
152 Subtask {
153 status: Status,
154 },
155 StreamRead {
156 code: ReturnCode,
157 pending: Option<(TypeStreamTableIndex, u32)>,
158 },
159 StreamWrite {
160 code: ReturnCode,
161 pending: Option<(TypeStreamTableIndex, u32)>,
162 },
163 FutureRead {
164 code: ReturnCode,
165 pending: Option<(TypeFutureTableIndex, u32)>,
166 },
167 FutureWrite {
168 code: ReturnCode,
169 pending: Option<(TypeFutureTableIndex, u32)>,
170 },
171 Cancelled,
172}
173
174impl Event {
175 fn parts(self) -> (u32, u32) {
180 const EVENT_NONE: u32 = 0;
181 const EVENT_SUBTASK: u32 = 1;
182 const EVENT_STREAM_READ: u32 = 2;
183 const EVENT_STREAM_WRITE: u32 = 3;
184 const EVENT_FUTURE_READ: u32 = 4;
185 const EVENT_FUTURE_WRITE: u32 = 5;
186 const EVENT_CANCELLED: u32 = 6;
187 match self {
188 Event::None => (EVENT_NONE, 0),
189 Event::Cancelled => (EVENT_CANCELLED, 0),
190 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
191 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
192 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
193 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
194 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
195 }
196 }
197}
198
199mod callback_code {
201 pub const EXIT: u32 = 0;
202 pub const YIELD: u32 = 1;
203 pub const WAIT: u32 = 2;
204}
205
206const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
210
211pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
217 store: StoreContextMut<'a, T>,
218 get_data: fn(&mut T) -> D::Data<'_>,
219}
220
221impl<'a, T, D> Access<'a, T, D>
222where
223 D: HasData + ?Sized,
224 T: 'static,
225{
226 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
228 Self { store, get_data }
229 }
230
231 pub fn data_mut(&mut self) -> &mut T {
233 self.store.data_mut()
234 }
235
236 pub fn get(&mut self) -> D::Data<'_> {
238 (self.get_data)(self.data_mut())
239 }
240
241 pub fn spawn(&mut self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
245 where
246 T: 'static,
247 {
248 let accessor = Accessor {
249 get_data: self.get_data,
250 token: StoreToken::new(self.store.as_context_mut()),
251 };
252 self.store
253 .as_context_mut()
254 .spawn_with_accessor(accessor, task)
255 }
256
257 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
260 self.get_data
261 }
262}
263
264impl<'a, T, D> AsContext for Access<'a, T, D>
265where
266 D: HasData + ?Sized,
267 T: 'static,
268{
269 type Data = T;
270
271 fn as_context(&self) -> StoreContext<'_, T> {
272 self.store.as_context()
273 }
274}
275
276impl<'a, T, D> AsContextMut for Access<'a, T, D>
277where
278 D: HasData + ?Sized,
279 T: 'static,
280{
281 fn as_context_mut(&mut self) -> StoreContextMut<'_, T> {
282 self.store.as_context_mut()
283 }
284}
285
286pub struct Accessor<T: 'static, D = HasSelf<T>>
346where
347 D: HasData + ?Sized,
348{
349 token: StoreToken<T>,
350 get_data: fn(&mut T) -> D::Data<'_>,
351}
352
353pub trait AsAccessor {
370 type Data: 'static;
372
373 type AccessorData: HasData + ?Sized;
376
377 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData>;
379}
380
381impl<T: AsAccessor + ?Sized> AsAccessor for &T {
382 type Data = T::Data;
383 type AccessorData = T::AccessorData;
384
385 fn as_accessor(&self) -> &Accessor<Self::Data, Self::AccessorData> {
386 T::as_accessor(self)
387 }
388}
389
390impl<T, D: HasData + ?Sized> AsAccessor for Accessor<T, D> {
391 type Data = T;
392 type AccessorData = D;
393
394 fn as_accessor(&self) -> &Accessor<T, D> {
395 self
396 }
397}
398
399const _: () = {
422 const fn assert<T: Send + Sync>() {}
423 assert::<Accessor<UnsafeCell<u32>>>();
424};
425
426impl<T> Accessor<T> {
427 pub(crate) fn new(token: StoreToken<T>) -> Self {
436 Self {
437 token,
438 get_data: |x| x,
439 }
440 }
441}
442
443impl<T, D> Accessor<T, D>
444where
445 D: HasData + ?Sized,
446{
447 pub fn with<R>(&self, fun: impl FnOnce(Access<'_, T, D>) -> R) -> R {
465 tls::get(|vmstore| {
466 fun(Access {
467 store: self.token.as_context_mut(vmstore),
468 get_data: self.get_data,
469 })
470 })
471 }
472
473 pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
476 self.get_data
477 }
478
479 pub fn with_getter<D2: HasData>(
496 &self,
497 get_data: fn(&mut T) -> D2::Data<'_>,
498 ) -> Accessor<T, D2> {
499 Accessor {
500 token: self.token,
501 get_data,
502 }
503 }
504
505 pub fn spawn(&self, task: impl for<'fut> AccessorTask<'fut, T, D>) -> Result<JoinHandle>
521 where
522 T: 'static,
523 {
524 let accessor = self.clone_for_spawn();
525 self.with(|mut access| access.as_context_mut().spawn_with_accessor(accessor, task))
526 }
527
528 fn clone_for_spawn(&self) -> Self {
529 Self {
530 token: self.token,
531 get_data: self.get_data,
532 }
533 }
534
535 pub fn poll_no_interesting_tasks(&self, cx: &mut Context<'_>) -> Poll<()> {
571 self.with(|mut access| {
572 let store = access.as_context_mut().0;
573 let state = store.concurrent_state_mut_without_forcing_current_thread();
574 if state.interesting_tasks == 0 {
575 Poll::Ready(())
576 } else {
577 state.interesting_tasks_empty_waker = Some(cx.waker().clone());
578 Poll::Pending
579 }
580 })
581 }
582
583 pub fn poll_ready_for_concurrent_call(&self, func: Func, cx: &mut Context<'_>) -> Poll<()> {
600 self.with(|mut access| {
601 let store = access.as_context_mut().0;
602 let (_, _, _, raw_options) = func.abi_info(store);
603 let instance = func.instance().runtime_instance(raw_options.instance);
604 let state = store.instance_state(instance).concurrent_state();
605 if state.backpressure == 0 {
606 Poll::Ready(())
607 } else {
608 store
609 .concurrent_state_mut_without_forcing_current_thread()
610 .ready_for_concurrent_call_waker = Some(cx.waker().clone());
611 Poll::Pending
612 }
613 })
614 }
615}
616
617pub trait AccessorTask<'fut, T, D = HasSelf<T>>:
639 AsyncFnOnce(&Accessor<T, D>) -> Result<()> + Send + 'static
640where
641 D: HasData + ?Sized,
642{
643 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut;
645}
646
647impl<'fut, F, Fut, T, D> AccessorTask<'fut, T, D> for F
648where
649 T: 'static,
650 F: AsyncFnOnce(&Accessor<T, D>) -> Result<()>,
651 F: FnOnce(&'fut Accessor<T, D>) -> Fut + Send + 'static,
652 Fut: Future<Output = Result<()>> + Send + 'fut,
653 D: HasData,
654{
655 fn run(self, accessor: &'fut Accessor<T, D>) -> impl Future<Output = Result<()>> + Send + 'fut {
656 (self)(accessor)
657 }
658}
659
660enum CallerInfo {
663 Async {
665 params: Vec<ValRaw>,
666 has_result: bool,
667 },
668 Sync {
670 params: Vec<ValRaw>,
671 result_count: u32,
672 },
673}
674
675enum WaitMode {
677 Fiber(StoreFiber<'static>),
679 Callback(Instance),
682}
683
684impl fmt::Debug for WaitMode {
685 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
686 match self {
687 Self::Fiber(_) => f.debug_tuple("Fiber").finish(),
688 Self::Callback(instance) => f.debug_tuple("Callback").field(instance).finish(),
689 }
690 }
691}
692
693#[derive(Debug)]
695enum SuspendReason {
696 Waiting {
699 set: TableId<WaitableSet>,
700 thread: QualifiedThreadId,
701 },
702 YieldingToSubtask { thread: QualifiedThreadId },
705 NeedWork,
708 Yielding { thread: QualifiedThreadId },
711 ExplicitlySuspending { thread: QualifiedThreadId },
714}
715
716enum GuestCallKind {
718 DeliverEvent {
721 instance: Instance,
723 set: Option<TableId<WaitableSet>>,
728 },
729 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
735 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
736}
737
738impl fmt::Debug for GuestCallKind {
739 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
740 match self {
741 Self::DeliverEvent { instance, set } => f
742 .debug_struct("DeliverEvent")
743 .field("instance", instance)
744 .field("set", set)
745 .finish(),
746 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
747 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
748 }
749 }
750}
751
752#[derive(Copy, Clone, Debug)]
754pub enum SuspensionTarget {
755 Resume(u32),
756 Promote(u32),
757 None,
758}
759
760#[derive(Copy, Clone, Debug)]
762pub enum ResumeThread {
763 Promote,
764 Resume,
765 ResumeLater,
766}
767
768#[derive(Debug)]
770struct GuestCall {
771 thread: QualifiedThreadId,
772 kind: GuestCallKind,
773}
774
775impl GuestCall {
776 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
786 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
787 let async_typed = task.async_typed;
788 let instance = task.instance;
789 let state = store.instance_state(instance).concurrent_state();
790
791 let ready = match &self.kind {
792 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
793 GuestCallKind::StartImplicit(_) => {
794 !async_typed || !(state.do_not_enter || state.backpressure > 0)
795 }
796 GuestCallKind::StartExplicit(_) => true,
797 };
798 log::trace!(
799 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
800 state.do_not_enter,
801 state.backpressure
802 );
803 Ok(ready)
804 }
805}
806
807enum WorkerItem {
809 GuestCall(GuestCall),
810 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
811}
812
813enum WorkItem {
816 PushFuture(AlwaysMut<HostTaskFuture>),
818 ResumeFiber {
820 instance: RuntimeInstance,
821 thread: QualifiedThreadId,
822 fiber: StoreFiber<'static>,
823 },
824 ResumeThread {
826 instance: RuntimeInstance,
827 thread: QualifiedThreadId,
828 },
829 GuestCall {
831 instance: RuntimeInstance,
832 call: GuestCall,
833 },
834 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
836}
837
838impl fmt::Debug for WorkItem {
839 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
840 match self {
841 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
842 Self::ResumeFiber {
843 instance, thread, ..
844 } => f
845 .debug_struct("ResumeFiber")
846 .field("instance", instance)
847 .field("thread", thread)
848 .finish(),
849 Self::ResumeThread { instance, thread } => f
850 .debug_struct("ResumeThread")
851 .field("instance", instance)
852 .field("thread", thread)
853 .finish(),
854 Self::GuestCall { instance, call } => f
855 .debug_struct("GuestCall")
856 .field("instance", instance)
857 .field("call", call)
858 .finish(),
859 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
860 }
861 }
862}
863
864#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
866pub(crate) enum WaitResult {
867 Cancelled,
868 Completed,
869}
870
871pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
879 store: &mut dyn VMStore,
880 host_task: EnteredHostTask,
881 future: impl Future<Output = Result<R>> + Send + 'static,
882) -> Result<R> {
883 let mut future = Box::pin(future);
890 let poll = tls::set(store, || {
891 future
892 .as_mut()
893 .poll(&mut Context::from_waker(&Waker::noop()))
894 });
895
896 let caller = match host_task {
897 Some(caller) => caller,
898 None => bail_bug!("host task wasn't created but should have been"),
899 };
900
901 let task = match poll {
902 Poll::Ready(result) => return result,
904
905 Poll::Pending => {
910 let Some(task) = store.materialize_host_task_id()? else {
911 bail_bug!("current thread is not a host thread")
912 };
913
914 let future = Box::pin(async move {
917 let result = run_with_host_task_set(task, future).await??;
918 tls::get(move |store| {
919 let state = store.concurrent_state_mut()?;
920 let host_state = &mut state.get_mut(task)?.state;
921 assert!(matches!(host_state, HostTaskState::CalleeStarted));
922 *host_state = HostTaskState::CalleeFinished(Box::new(result));
923
924 Waitable::Host(task).set_event(
925 state,
926 Some(Event::Subtask {
927 status: Status::Returned,
928 }),
929 )?;
930
931 Ok(())
932 })
933 }) as HostTaskFuture;
934
935 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
936 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
937
938 let state = store.concurrent_state_mut()?;
939 state.push_future(future);
940
941 let set = state.get_mut(caller.thread)?.sync_call_set;
942 Waitable::Host(task).join(state, Some(set))?;
943
944 store.suspend(SuspendReason::Waiting {
945 set,
946 thread: caller,
947 })?;
948
949 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
953 task
954 }
955 };
956
957 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
959 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
960 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
961 Ok(result) => *result,
962 Err(_) => bail_bug!("host task finished with wrong type of result"),
963 }),
964 _ => bail_bug!("unexpected host task state after completion"),
965 }
966}
967
968fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
970 match call.kind {
971 GuestCallKind::DeliverEvent { instance, set } => {
972 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
973 Some(pair) => pair,
974 None => bail_bug!("delivering non-present event"),
975 };
976 let state = store.concurrent_state_mut()?;
977 let task = state.get_mut(call.thread.task)?;
978 let runtime_instance = task.instance;
979 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
980
981 log::trace!(
982 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
983 call.thread,
984 );
985
986 let old_thread = store.set_thread(call.thread)?;
987 log::trace!(
988 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
989 call.thread
990 );
991
992 store.enter_instance(runtime_instance);
993
994 let Some(callback) = store
995 .concurrent_state_mut()?
996 .get_mut(call.thread.task)?
997 .callback
998 .take()
999 else {
1000 bail_bug!("guest task callback field not present")
1001 };
1002
1003 let code = callback(store, event, handle)?;
1004
1005 store
1006 .concurrent_state_mut()?
1007 .get_mut(call.thread.task)?
1008 .callback = Some(callback);
1009
1010 store.exit_instance(runtime_instance)?;
1011
1012 store.set_thread(old_thread)?;
1013
1014 instance.handle_callback_code(store, call.thread, runtime_instance.index, code)?;
1015
1016 log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
1017 }
1018 GuestCallKind::StartImplicit(fun) => {
1019 fun(store)?;
1020 }
1021 GuestCallKind::StartExplicit(fun) => {
1022 fun(store)?;
1023 }
1024 }
1025
1026 Ok(())
1027}
1028
1029impl<T> Store<T> {
1030 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1032 where
1033 T: Send + 'static,
1034 {
1035 ensure!(
1036 self.as_context().0.concurrency_support(),
1037 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1038 );
1039 self.as_context_mut().run_concurrent(fun).await
1040 }
1041
1042 #[doc(hidden)]
1043 pub fn assert_concurrent_state_empty(&mut self) {
1044 self.as_context_mut().assert_concurrent_state_empty();
1045 }
1046
1047 #[doc(hidden)]
1048 pub fn concurrent_state_table_size(&mut self) -> usize {
1049 self.as_context_mut().concurrent_state_table_size()
1050 }
1051
1052 pub fn spawn(
1054 &mut self,
1055 task: impl for<'fut> AccessorTask<'fut, T, HasSelf<T>>,
1056 ) -> Result<JoinHandle>
1057 where
1058 T: 'static,
1059 {
1060 self.as_context_mut().spawn(task)
1061 }
1062}
1063
1064impl<T> StoreContextMut<'_, T> {
1065 #[doc(hidden)]
1076 pub fn assert_concurrent_state_empty(self) {
1077 let store = self.0;
1078 store
1079 .store_data_mut()
1080 .components
1081 .assert_instance_states_empty();
1082 let state = store.concurrent_state_mut().unwrap();
1083 assert!(
1084 state.table.get_mut().is_empty(),
1085 "non-empty table: {:?}",
1086 state.table.get_mut()
1087 );
1088 assert!(state.switch_item.is_none());
1089 assert!(state.next_switch_item.is_none());
1090 assert!(state.high_priority.is_empty());
1091 assert!(state.low_priority.is_empty());
1092 assert!(state.unforced_current_thread.is_none());
1093 assert!(state.deferred_host_call_context.is_none());
1094 assert!(state.futures_mut().unwrap().is_empty());
1095 assert!(state.global_error_context_ref_counts.is_empty());
1096 }
1097
1098 #[doc(hidden)]
1103 pub fn concurrent_state_table_size(&mut self) -> usize {
1104 self.0
1105 .concurrent_state_mut()
1106 .unwrap()
1107 .table
1108 .get_mut()
1109 .iter_mut()
1110 .count()
1111 }
1112
1113 pub fn spawn(mut self, task: impl for<'fut> AccessorTask<'fut, T>) -> Result<JoinHandle>
1123 where
1124 T: 'static,
1125 {
1126 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1127 self.spawn_with_accessor(accessor, task)
1128 }
1129
1130 fn spawn_with_accessor<D>(
1133 self,
1134 accessor: Accessor<T, D>,
1135 task: impl for<'fut> AccessorTask<'fut, T, D>,
1136 ) -> Result<JoinHandle>
1137 where
1138 T: 'static,
1139 D: HasData + ?Sized,
1140 {
1141 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1145 self.0
1146 .concurrent_state_mut()?
1147 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1148 Ok(handle)
1149 }
1150
1151 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1235 where
1236 T: Send + 'static,
1237 {
1238 ensure!(
1239 self.0.concurrency_support(),
1240 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1241 );
1242 self.do_run_concurrent(fun, false).await
1243 }
1244
1245 pub(super) async fn run_concurrent_trap_on_idle<R>(
1246 self,
1247 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1248 ) -> Result<R> {
1249 self.do_run_concurrent(fun, true).await
1250 }
1251
1252 async fn do_run_concurrent<R>(
1253 mut self,
1254 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1255 trap_on_idle: bool,
1256 ) -> Result<R> {
1257 debug_assert!(self.0.concurrency_support());
1258 let already_running = self
1259 .0
1260 .concurrent_state_mut_already_forced_current_thread()
1261 .event_loop_running;
1262 if already_running {
1263 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1264 }
1265 let token = StoreToken::new(self.as_context_mut());
1266
1267 struct Dropper<'a, T: 'static, V> {
1268 store: StoreContextMut<'a, T>,
1269 value: ManuallyDrop<V>,
1270 }
1271
1272 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1273 fn drop(&mut self) {
1274 self.store
1275 .0
1276 .concurrent_state_mut_already_forced_current_thread()
1277 .event_loop_running = false;
1278
1279 tls::set(self.store.0, || {
1280 unsafe { ManuallyDrop::drop(&mut self.value) }
1285 });
1286 }
1287 }
1288
1289 let accessor = &Accessor::new(token);
1290 self.0
1291 .concurrent_state_mut_already_forced_current_thread()
1292 .event_loop_running = true;
1293 let dropper = &mut Dropper {
1294 store: self,
1295 value: ManuallyDrop::new(fun(accessor)),
1296 };
1297 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1299
1300 dropper
1301 .store
1302 .as_context_mut()
1303 .poll_until(future, trap_on_idle)
1304 .await
1305 }
1306
1307 async fn poll_until<R>(
1313 mut self,
1314 mut future: Pin<&mut impl Future<Output = R>>,
1315 trap_on_idle: bool,
1316 ) -> Result<R> {
1317 struct Reset<'a, T: 'static> {
1318 store: StoreContextMut<'a, T>,
1319 futures: Option<FuturesUnordered<HostTaskFuture>>,
1320 }
1321
1322 impl<'a, T> Drop for Reset<'a, T> {
1323 fn drop(&mut self) {
1324 if let Some(futures) = self.futures.take() {
1325 *self
1326 .store
1327 .0
1328 .concurrent_state_mut_already_forced_current_thread()
1329 .futures
1330 .get_mut() = Some(futures);
1331 }
1332 }
1333 }
1334
1335 loop {
1336 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1340 let mut reset = Reset {
1341 store: self.as_context_mut(),
1342 futures,
1343 };
1344 let mut next = match reset.futures.as_mut() {
1345 Some(f) => pin!(f.next()),
1346 None => bail_bug!("concurrent state missing futures field"),
1347 };
1348
1349 enum PollResult<R> {
1350 Complete(R),
1351 ProcessWork {
1352 ready: Option<WorkItem>,
1353 low_priority: bool,
1354 },
1355 }
1356
1357 let result = future::poll_fn(|cx| {
1358 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1361 return Poll::Ready(Ok(PollResult::Complete(value)));
1362 }
1363
1364 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1368 Poll::Ready(Some(output)) => {
1369 match output {
1370 Err(e) => return Poll::Ready(Err(e)),
1371 Ok(()) => {}
1372 }
1373 Poll::Ready(true)
1374 }
1375 Poll::Ready(None) => Poll::Ready(false),
1376 Poll::Pending => Poll::Pending,
1377 };
1378
1379 let state = reset.store.0.concurrent_state_mut()?;
1394 let mut ready = state.switch_item.take();
1395 let mut low_priority = false;
1396 if ready.is_none() {
1397 ready = state.high_priority.pop_back();
1398 if ready.is_none() {
1399 ready = state.low_priority.pop_back();
1400 low_priority = true;
1401 }
1402 }
1403 if ready.is_some() {
1404 return Poll::Ready(Ok(PollResult::ProcessWork {
1405 ready,
1406 low_priority,
1407 }));
1408 }
1409
1410 return match next {
1414 Poll::Ready(true) => {
1415 Poll::Ready(Ok(PollResult::ProcessWork {
1421 ready: None,
1422 low_priority: false,
1423 }))
1424 }
1425 Poll::Ready(false) => {
1426 if let Poll::Ready(value) =
1430 tls::set(reset.store.0, || future.as_mut().poll(cx))
1431 {
1432 Poll::Ready(Ok(PollResult::Complete(value)))
1433 } else {
1434 if trap_on_idle {
1440 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1447 Trap::CannotBlockSyncTask.into()
1448 } else {
1449 Trap::AsyncDeadlock.into()
1451 }))
1452 } else {
1453 Poll::Pending
1457 }
1458 }
1459 }
1460 Poll::Pending => Poll::Pending,
1465 };
1466 })
1467 .await;
1468
1469 drop(reset);
1473
1474 match result? {
1475 PollResult::Complete(value) => break Ok(value),
1478 PollResult::ProcessWork {
1481 ready,
1482 low_priority,
1483 } => {
1484 struct Dispose<'a, T: 'static> {
1485 store: StoreContextMut<'a, T>,
1486 ready: Option<WorkItem>,
1487 }
1488
1489 impl<'a, T> Drop for Dispose<'a, T> {
1490 fn drop(&mut self) {
1491 if let Some(item) = self.ready.take() {
1492 match item {
1493 WorkItem::ResumeFiber { mut fiber, .. } => {
1494 fiber.dispose(self.store.0)
1495 }
1496 WorkItem::PushFuture(future) => {
1497 tls::set(self.store.0, move || drop(future))
1498 }
1499 _ => {}
1500 }
1501 }
1502 }
1503 }
1504
1505 let mut dispose = Dispose {
1506 store: self.as_context_mut(),
1507 ready,
1508 };
1509
1510 if low_priority {
1532 dispose.store.0.yield_now().await
1533 }
1534
1535 if let Some(item) = dispose.ready.take() {
1536 dispose
1537 .store
1538 .as_context_mut()
1539 .handle_work_item(item)
1540 .await?;
1541 }
1542 }
1543 }
1544 }
1545 }
1546
1547 async fn handle_work_item(self, item: WorkItem) -> Result<()> {
1549 log::trace!("handle work item {item:?}");
1550 match item {
1551 WorkItem::PushFuture(future) => {
1552 self.0
1553 .concurrent_state_mut()?
1554 .futures_mut()?
1555 .push(future.into_inner());
1556 }
1557 WorkItem::ResumeFiber { fiber, .. } => {
1558 self.0.resume_fiber(fiber).await?;
1559 }
1560 WorkItem::ResumeThread { thread, .. } => {
1561 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1562 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1563 GuestThreadState::Running,
1564 ) {
1565 self.0.resume_fiber(fiber).await?;
1566 } else {
1567 bail_bug!("cannot resume non-pending thread {thread:?}");
1568 }
1569 }
1570 WorkItem::GuestCall { call, .. } => {
1571 if call.is_ready(self.0)? {
1572 self.0
1573 .concurrent_state_mut()?
1574 .get_mut(call.thread.thread)?
1575 .wake_on_cancel = WakeOnCancel::None;
1576 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1577 } else {
1578 let state = self.0.concurrent_state_mut()?;
1579 let task = state.get_mut(call.thread.task)?;
1580 if !task.starting_sent {
1581 task.starting_sent = true;
1582 if let GuestCallKind::StartImplicit(_) = &call.kind {
1583 Waitable::Guest(call.thread.task).set_event(
1584 state,
1585 Some(Event::Subtask {
1586 status: Status::Starting,
1587 }),
1588 )?;
1589 }
1590 }
1591
1592 let instance = state.get_mut(call.thread.task)?.instance;
1593 self.0
1594 .instance_state(instance)
1595 .concurrent_state()
1596 .pending
1597 .insert(call.thread, call.kind);
1598
1599 self.0.concurrent_state_mut()?.take_next_switch_item()?;
1603 }
1604 }
1605 WorkItem::WorkerFunction(fun) => {
1606 self.run_on_worker(WorkerItem::Function(fun)).await?;
1607 }
1608 }
1609
1610 Ok(())
1611 }
1612
1613 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1615 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1616 fiber
1617 } else {
1618 unsafe {
1637 fiber::make_fiber_unchecked(self.0, move |store| {
1638 loop {
1639 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1640 bail_bug!("worker_item not present when resuming fiber")
1641 };
1642 match item {
1643 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1644 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1645 }
1646
1647 store.suspend(SuspendReason::NeedWork)?;
1648 }
1649 })?
1650 }
1651 };
1652
1653 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1654 assert!(worker_item.is_none());
1655 *worker_item = Some(item);
1656
1657 self.0.resume_fiber(worker).await
1658 }
1659
1660 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1665 where
1666 T: 'static,
1667 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1668 + Send
1669 + Sync
1670 + 'static,
1671 R: Send + Sync + 'static,
1672 {
1673 let token = StoreToken::new(self);
1674 async move {
1675 let mut accessor = Accessor::new(token);
1676 closure(&mut accessor).await
1677 }
1678 }
1679
1680 pub fn async_call_stack(&mut self) -> Result<impl Iterator<Item = GuestTaskId>> {
1702 let mut cur = Some(self.0.current_thread()?);
1703 let state = self.0.concurrent_state_mut()?;
1704 Ok(core::iter::from_fn(move || {
1705 while let Some(t) = cur {
1706 cur = state.parent(t);
1707 if let Some(task) = t.guest_task() {
1708 return Some(GuestTaskId(task));
1709 }
1710 }
1711
1712 None
1713 }))
1714 }
1715
1716 pub(crate) async fn start_instance(
1717 &mut self,
1718 instance: ModuleInstance,
1719 ) -> Result<ModuleInstance> {
1720 let (tx, rx) = oneshot::channel();
1721 let token = StoreToken::new(self.as_context_mut());
1722 self.0.queue_task(move |store| {
1723 _ = tx.send(
1724 instance
1725 .start_raw(&mut token.as_context_mut(store))
1726 .map(|()| instance),
1727 );
1728 Ok(())
1729 })?;
1730 self.as_context_mut()
1731 .run_concurrent_trap_on_idle(async |_| {
1732 rx.await
1733 .map_err(|_| format_err!("oneshot channel canceled"))
1734 })
1735 .await??
1736 }
1737}
1738
1739pub type EnteredHostTask = Option<QualifiedThreadId>;
1746
1747impl StoreOpaque {
1748 #[inline]
1752 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1753 if !self.concurrency_support() {
1755 return Ok(CurrentThread::None);
1756 }
1757
1758 if !self
1761 .vm_store_context_mut()
1762 .current_thread_mut()
1763 .is_deferred()
1764 {
1765 return Ok(self
1766 .concurrent_state_mut_already_forced_current_thread()
1767 .unforced_current_thread);
1768 }
1769
1770 self.force_deferred_current_thread()
1771 }
1772
1773 #[cold]
1776 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1777 let state = self.concurrent_state_mut_without_forcing_current_thread();
1786 let id = match state.unforced_current_thread.guest_task() {
1787 Some(task) => state.get_mut(task)?.instance.instance,
1788 None => bail_bug!("deferred component-model thread with non-guest base"),
1789 };
1790
1791 let mut frames = Vec::new();
1794 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1795 while let Some(ptr) = cur.as_deferred() {
1796 let deferred = unsafe { ptr.as_non_null().as_ref() };
1801 frames.push((
1802 deferred.callee_async != 0,
1803 deferred.callee_instance,
1804 deferred.saved_context,
1805 ));
1806 cur = deferred.parent;
1807 }
1808
1809 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1813
1814 let current_context = *self.vm_store_context_mut().component_context_mut();
1817
1818 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1822 *self.vm_store_context_mut().component_context_mut() = saved_context;
1826 let callee = RuntimeInstance {
1827 instance: id,
1828 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1829 };
1830 self.enter_guest_sync_call(callee_async, callee)?;
1831 }
1832
1833 *self.vm_store_context_mut().component_context_mut() = current_context;
1835
1836 Ok(self
1837 .concurrent_state_mut_without_forcing_current_thread()
1838 .unforced_current_thread)
1839 }
1840
1841 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1842 match self.current_thread()?.guest() {
1843 Some(id) => Ok(*id),
1844 None => bail_bug!("current thread is not a guest thread"),
1845 }
1846 }
1847
1848 pub(crate) fn current_materialized_host_task(&mut self) -> Result<Option<TableId<HostTask>>> {
1852 match self.current_thread()? {
1853 CurrentThread::Host(id) => Ok(Some(id)),
1854 CurrentThread::DeferredHost(_) | CurrentThread::None => Ok(None),
1855 _ => bail_bug!("current thread is not a host thread"),
1856 }
1857 }
1858
1859 fn materialize_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
1862 Ok(self
1863 .concurrent_state_mut()?
1864 .materialize_current_host_task_id()?)
1865 }
1866
1867 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1868 log::trace!("enter sync-typed call {callee:?}");
1869 let state = self.instance_state(callee).concurrent_state();
1870 let old_do_not_suspend = state.do_not_suspend;
1871 state.do_not_suspend = true;
1872
1873 let thread = self.current_guest_thread()?;
1874 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1875 if thread.old_do_not_suspend.is_some() {
1876 bail_bug!("current thread already has `old_do_not_suspend` value");
1877 }
1878
1879 thread.old_do_not_suspend = Some(old_do_not_suspend);
1880
1881 Ok(())
1882 }
1883
1884 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1885 log::trace!("exit sync-typed call {callee:?}");
1886 let thread = self.current_guest_thread()?;
1887 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1888 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1889 bail_bug!("current thread missing `old_do_not_suspend` value");
1890 };
1891 let state = self.instance_state(callee).concurrent_state();
1892 state.do_not_suspend = old_do_not_suspend;
1893 Ok(())
1894 }
1895
1896 pub(crate) fn enter_guest_sync_call(
1908 &mut self,
1909 callee_async_typed: bool,
1910 callee: RuntimeInstance,
1911 ) -> Result<()> {
1912 log::trace!("enter sync-lifted call {callee:?}");
1913 if !self.concurrency_support() {
1914 return self.enter_call_not_concurrent();
1915 }
1916
1917 let thread = self.current_thread()?;
1918 let caller = if let Some(thread) = thread.guest() {
1919 Caller::Guest { thread: *thread }
1920 } else {
1921 Caller::Host {
1922 tx: None,
1923 host_future_present: false,
1924 caller: self.materialize_host_task_id()?,
1925 }
1926 };
1927 let state = self.concurrent_state_mut()?;
1928 let guest_thread = GuestTask::new(
1929 state,
1930 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1931 LiftResult {
1932 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1933 ty: TypeTupleIndex::reserved_value(),
1934 memory: None,
1935 string_encoding: StringEncoding::Utf8,
1936 },
1937 caller,
1938 None,
1939 callee,
1940 callee_async_typed,
1941 true,
1942 )?;
1943
1944 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1945 guest_thread.thread,
1946 self,
1947 callee.index,
1948 )?;
1949 self.set_thread(guest_thread)?;
1950
1951 if !callee_async_typed {
1952 self.enter_sync_call(callee)?;
1953 }
1954
1955 Ok(())
1956 }
1957
1958 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1966 if !self.concurrency_support() {
1967 return Ok(self.exit_call_not_concurrent());
1968 }
1969
1970 let thread = match self.current_thread()?.guest() {
1971 Some(t) => *t,
1972 None => bail_bug!("expected task when exiting"),
1973 };
1974 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1975 let instance = task.instance;
1976
1977 let caller = match &task.caller {
1978 &Caller::Guest { thread } => thread.into(),
1979 &Caller::Host { caller, .. } => caller
1980 .map(CurrentThread::Host)
1981 .unwrap_or(CurrentThread::None),
1982 };
1983 task.lift_result = None;
1984 task.exited = true;
1985 let async_typed = task.async_typed;
1986
1987 if !async_typed {
1988 self.exit_sync_call(instance)?;
1989 }
1990
1991 self.set_thread(caller)?;
1992
1993 log::trace!("exit sync-lifted call {instance:?}");
1994
1995 if async_typed {
1996 self.switch_or_trap_if_may_not_suspend(instance)?;
2001 }
2002
2003 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
2004
2005 Ok(())
2006 }
2007
2008 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
2015 if !self.concurrency_support() {
2016 self.enter_call_not_concurrent()?;
2017 return Ok(None);
2018 }
2019 let caller = self.current_guest_thread()?;
2020 log::trace!("new deferred host task with caller {caller:?}");
2021 self.set_thread(CurrentThread::DeferredHost(caller))?;
2022 let state = self.concurrent_state_mut()?;
2023 debug_assert!(state.deferred_host_call_context.is_none());
2024 state.deferred_host_call_context = Some(CallContext::default());
2025 state.debug_assert_deferred_host_invariant();
2026 Ok(Some(caller))
2027 }
2028
2029 pub(crate) fn host_task_delete(
2036 &mut self,
2037 original_task: EnteredHostTask,
2038 materialized_task: Option<TableId<HostTask>>,
2039 ) -> Result<()> {
2040 match original_task {
2041 Some(caller) => {
2042 self.set_thread(caller)?;
2043 if materialized_task.is_none() {
2044 let state = self.concurrent_state_mut()?;
2045 let context = state
2046 .deferred_host_call_context
2047 .take()
2048 .expect("deferred host call context should be present");
2049 debug_assert!(context.is_empty());
2050 state.debug_assert_deferred_host_invariant();
2051 }
2052 log::trace!(
2053 "delete host task with caller {original_task:?} and materialized as {materialized_task:?}"
2054 );
2055 if let Some(task) = materialized_task {
2056 self.concurrent_state_mut()?.delete(task)?;
2057 }
2058 }
2059 None => {
2060 debug_assert!(materialized_task.is_none());
2061 self.exit_call_not_concurrent();
2062 }
2063 }
2064 Ok(())
2065 }
2066
2067 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2070 self.component_instance_mut(instance.instance)
2071 .instance_state(instance.index)
2072 }
2073
2074 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2080 let thread = thread.into();
2081 let state = self.concurrent_state_mut()?;
2082 state.debug_assert_deferred_host_invariant();
2083 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2084
2085 if let Some(old_thread) = old_thread.guest() {
2093 let old_context = *self.vm_store_context_mut().component_context_mut();
2094 self.concurrent_state_mut()?
2095 .get_mut(old_thread.thread)?
2096 .context = old_context;
2097 }
2098 if cfg!(debug_assertions) {
2099 *self.vm_store_context_mut().component_context_mut() =
2100 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2101 }
2102 if let Some(thread) = thread.guest() {
2103 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2104 let context = thread.context;
2105 if cfg!(debug_assertions) {
2106 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2107 }
2108 *self.vm_store_context_mut().component_context_mut() = context;
2109 }
2110
2111 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2113 VMLazyThread::none()
2114 } else {
2115 VMLazyThread::forced()
2116 };
2117
2118 Ok(old_thread)
2119 }
2120
2121 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2123 if self.switch_if_may_not_suspend(instance)? {
2124 Ok(())
2125 } else {
2126 Err(Trap::CannotBlockSyncTask.into())
2127 }
2128 }
2129
2130 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2134 self.concurrent_state_mut()?;
2138
2139 Ok(!self.concurrency_support()
2140 || !self
2141 .instance_state(instance)
2142 .concurrent_state()
2143 .do_not_suspend
2144 || self
2145 .concurrent_state_mut()?
2146 .promote_instance_local_thread_work_item(instance)?)
2147 }
2148
2149 fn enter_instance(&mut self, instance: RuntimeInstance) {
2153 log::trace!("enter {instance:?}");
2154 self.instance_state(instance)
2155 .concurrent_state()
2156 .do_not_enter = true;
2157 }
2158
2159 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2163 log::trace!("exit {instance:?}");
2164 self.instance_state(instance)
2165 .concurrent_state()
2166 .do_not_enter = false;
2167 self.partition_pending(instance)
2168 }
2169
2170 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2178 for (thread, kind) in
2179 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2180 {
2181 let call = GuestCall { thread, kind };
2182 if call.is_ready(self)? {
2183 self.concurrent_state_mut()?
2184 .push_high_priority(WorkItem::GuestCall { instance, call });
2185 } else {
2186 self.instance_state(instance)
2187 .concurrent_state()
2188 .pending
2189 .insert(call.thread, call.kind);
2190 }
2191 }
2192
2193 if let Some(waker) = self
2194 .concurrent_state_mut()?
2195 .ready_for_concurrent_call_waker
2196 .take()
2197 {
2198 waker.wake();
2199 }
2200
2201 Ok(())
2202 }
2203
2204 pub(crate) fn backpressure_modify(
2206 &mut self,
2207 caller_instance: RuntimeInstance,
2208 modify: impl FnOnce(u16) -> Option<u16>,
2209 ) -> Result<()> {
2210 let state = self.instance_state(caller_instance).concurrent_state();
2211 let old = state.backpressure;
2212 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2213 state.backpressure = new;
2214
2215 if old > 0 && new == 0 {
2216 self.partition_pending(caller_instance)?;
2219 }
2220
2221 Ok(())
2222 }
2223
2224 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2227 let old_thread = self.current_thread()?;
2228 log::trace!("resume_fiber: save current thread {old_thread:?}");
2229
2230 let fiber = fiber::resolve_or_release(self, fiber).await?;
2231
2232 self.set_thread(old_thread)?;
2233
2234 let state = self.concurrent_state_mut()?;
2235
2236 if let Some(ot) = old_thread.guest() {
2237 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2238 }
2239 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2240
2241 if let Some(mut fiber) = fiber {
2242 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2243 let reason = match state.suspend_reason.take() {
2245 Some(r) => r,
2246 None => bail_bug!("suspend reason missing when resuming fiber"),
2247 };
2248 match reason {
2249 SuspendReason::NeedWork => {
2250 if state.worker.is_none() {
2251 state.worker = Some(fiber);
2252 } else {
2253 fiber.dispose(self);
2254 }
2255 }
2256 SuspendReason::Yielding { thread } => {
2257 state.get_mut(thread.thread)?.state = GuestThreadState::Ready { fiber };
2258 let instance = state.get_mut(thread.task)?.instance;
2259 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2260 }
2261 SuspendReason::ExplicitlySuspending { thread } => {
2262 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2263 }
2264 SuspendReason::Waiting { set, thread } => {
2265 let old = state
2266 .get_mut(set)?
2267 .waiting
2268 .insert(thread, WaitMode::Fiber(fiber));
2269 assert!(old.is_none());
2270 }
2271 SuspendReason::YieldingToSubtask { thread } => {
2272 let item = WorkItem::ResumeFiber {
2281 instance: state.get_mut(thread.task)?.instance,
2282 thread,
2283 fiber,
2284 };
2285
2286 if state.next_switch_item.replace(item).is_some() {
2287 bail_bug!(
2290 "`ConcurrentState::next_switch_item` was already `Some(_)` when \
2291 a thread wanted to wait on a subtask"
2292 );
2293 }
2294 }
2295 };
2296 } else {
2297 log::trace!("resume_fiber: fiber has exited");
2298 }
2299
2300 Ok(())
2301 }
2302
2303 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2309 log::trace!("suspend fiber: {reason:?}");
2310
2311 let state = self.concurrent_state_mut()?;
2312
2313 let (save_and_restore_thread, save_and_restore_next_switch_item) = match &reason {
2320 SuspendReason::Yielding { .. }
2321 | SuspendReason::Waiting { .. }
2322 | SuspendReason::ExplicitlySuspending { .. } => {
2323 if state.switch_item.is_none() {
2326 state.take_next_switch_item()?;
2327 }
2328
2329 (true, false)
2330 }
2331 SuspendReason::YieldingToSubtask { .. } => (true, true),
2332 SuspendReason::NeedWork => (false, false),
2333 };
2334
2335 let old_next_switch_item = if save_and_restore_next_switch_item {
2336 let item = state.next_switch_item.take();
2337 Some(state.push(item)?)
2341 } else {
2342 None
2343 };
2344
2345 let old_guest_thread = if save_and_restore_thread {
2346 self.current_thread()?
2347 } else {
2348 CurrentThread::None
2349 };
2350
2351 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2352 assert!(suspend_reason.is_none());
2353 *suspend_reason = Some(reason);
2354
2355 if !self.fiber_async_state_mut().can_block() {
2358 return Err(format_err!("future dropped"));
2359 }
2360
2361 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2362
2363 if save_and_restore_thread {
2364 self.set_thread(old_guest_thread)?;
2365 }
2366
2367 if let Some(item) = old_next_switch_item {
2368 let state = self.concurrent_state_mut()?;
2369 state.next_switch_item = state.delete(item)?;
2370 }
2371
2372 Ok(())
2373 }
2374
2375 fn wait_for_event(
2376 &mut self,
2377 caller_instance: RuntimeInstance,
2378 waitable: Waitable,
2379 ) -> Result<()> {
2380 let caller = self.current_guest_thread()?;
2381 let state = self.concurrent_state_mut()?;
2382
2383 waitable.trap_if_in_waitable_set(state)?;
2384
2385 let set = state.get_mut(caller.thread)?.sync_call_set;
2386 waitable.join(state, Some(set))?;
2387
2388 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2389
2390 self.suspend(SuspendReason::Waiting {
2391 set,
2392 thread: caller,
2393 })?;
2394 let state = self.concurrent_state_mut()?;
2395
2396 waitable.join(state, None)
2397 }
2398
2399 fn cleanup_thread(
2421 &mut self,
2422 guest_thread: QualifiedThreadId,
2423 runtime_instance: RuntimeInstance,
2424 cleanup_task: CleanupTask,
2425 ) -> Result<()> {
2426 let state = self.concurrent_state_mut()?;
2427 state.take_next_switch_item()?;
2430 let thread_data = state.get_mut(guest_thread.thread)?;
2431 let sync_call_set = thread_data.sync_call_set;
2432 if let Some(guest_id) = thread_data.instance_rep {
2433 self.instance_state(runtime_instance)
2434 .thread_handle_table()
2435 .guest_thread_remove(guest_id)?;
2436 }
2437 let state = self.concurrent_state_mut()?;
2438
2439 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2441 if let Some(Event::Subtask {
2442 status: Status::Returned | Status::ReturnCancelled,
2443 }) = waitable.common(state)?.event
2444 {
2445 waitable.delete_from(state)?;
2446 }
2447 }
2448
2449 state.delete(guest_thread.thread)?;
2450 state.delete(sync_call_set)?;
2451 let task = state.get_mut(guest_thread.task)?;
2452 task.threads.remove(&guest_thread.thread);
2453
2454 if task.threads.is_empty() && !task.returned_or_cancelled() {
2455 bail!(Trap::NoAsyncResult);
2456 }
2457 let ready_to_delete = task.ready_to_delete();
2458
2459 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2460 task.decremented_interesting_task_count = true;
2461
2462 debug_assert!(state.interesting_tasks > 0);
2463 state.interesting_tasks -= 1;
2464 if state.interesting_tasks == 0
2465 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2466 {
2467 waker.wake();
2468 }
2469 }
2470
2471 match cleanup_task {
2472 CleanupTask::Yes => {
2473 if ready_to_delete {
2474 Waitable::Guest(guest_thread.task).delete_from(state)?;
2475 }
2476 }
2477 CleanupTask::No => {}
2478 }
2479
2480 Ok(())
2481 }
2482
2483 fn cancel_guest_subtask_without_lowered_parameters(
2496 &mut self,
2497 caller_instance: RuntimeInstance,
2498 guest_task: TableId<GuestTask>,
2499 ) -> Result<()> {
2500 let concurrent_state = self.concurrent_state_mut()?;
2501 let task = concurrent_state.get_mut(guest_task)?;
2502 assert!(!task.already_lowered_parameters());
2503 task.lower_params = None;
2507 task.lift_result = None;
2508 task.exited = true;
2509 let instance = task.instance;
2510
2511 assert_eq!(1, task.threads.len());
2514 let thread = *task.threads.iter().next().unwrap();
2515 self.cleanup_thread(
2516 QualifiedThreadId {
2517 task: guest_task,
2518 thread,
2519 },
2520 caller_instance,
2521 CleanupTask::No,
2522 )?;
2523
2524 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2526 let pending_count = pending.len();
2527 pending.retain(|thread, _| thread.task != guest_task);
2528 if pending.len() == pending_count {
2530 bail!(Trap::SubtaskCancelAfterTerminal);
2531 }
2532 Ok(())
2533 }
2534
2535 pub(crate) fn current_scope(&mut self) -> Result<Option<CurrentScope>> {
2538 if !self.concurrency_support() {
2539 return Ok(self
2540 .current_scope_id_not_concurrent()?
2541 .map(|id| CurrentScope::Id(Scope::Id(id))));
2542 }
2543
2544 Ok(match self.current_thread()? {
2545 CurrentThread::Guest(id) => Some(CurrentScope::Id(Scope::Id(id.task.rep()))),
2546 CurrentThread::GuestTask(id) => Some(CurrentScope::Id(Scope::Id(id.rep()))),
2547 CurrentThread::Host(id) => Some(CurrentScope::Id(Scope::HostId(id.rep()))),
2548 CurrentThread::DeferredHost(_) => Some(CurrentScope::DeferredHost),
2549 CurrentThread::None => return Ok(None),
2550 })
2551 }
2552
2553 pub(crate) fn queue_task(
2554 &mut self,
2555 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2556 ) -> Result<()> {
2557 self.concurrent_state_mut()?
2558 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2559 Ok(())
2560 }
2561
2562 fn any_may_not_suspend(&mut self) -> Result<bool> {
2571 Ok(self
2579 .concurrent_state_mut()?
2580 .table
2581 .get_mut()
2582 .iter_mut()
2583 .filter_map(|entry| {
2584 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2585 Some(task.instance)
2586 } else {
2587 None
2588 }
2589 })
2590 .collect::<Vec<_>>()
2591 .into_iter()
2592 .any(|instance| {
2593 self.instance_state(instance)
2594 .concurrent_state()
2595 .do_not_suspend
2596 }))
2597 }
2598}
2599
2600enum CleanupTask {
2601 Yes,
2602 No,
2603}
2604
2605impl Instance {
2606 fn get_event(
2609 self,
2610 store: &mut StoreOpaque,
2611 guest_task: TableId<GuestTask>,
2612 set: Option<TableId<WaitableSet>>,
2613 cancellable: bool,
2614 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2615 let state = store.concurrent_state_mut()?;
2616
2617 let task = state.get_mut(guest_task)?;
2618 let event = &mut task.event;
2619 if let Some(ev) = event
2620 && (cancellable || !matches!(ev, Event::Cancelled))
2621 {
2622 log::trace!("deliver event {ev:?} to {guest_task:?}");
2623
2624 if matches!(ev, Event::Cancelled) {
2625 task.cancel_request_delivered = true;
2626 }
2627
2628 let ev = *ev;
2629 *event = None;
2630 return Ok(Some((ev, None)));
2631 }
2632
2633 let set = match set {
2634 Some(set) => set,
2635 None => return Ok(None),
2636 };
2637 let waitable = match state.get_mut(set)?.ready.pop_first() {
2638 Some(v) => v,
2639 None => return Ok(None),
2640 };
2641
2642 let common = waitable.common(state)?;
2643 let handle = match common.handle {
2644 Some(h) => h,
2645 None => bail_bug!("handle not set when delivering event"),
2646 };
2647 let event = match common.event.take() {
2648 Some(e) => e,
2649 None => bail_bug!("event not set when delivering event"),
2650 };
2651
2652 log::trace!(
2653 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2654 );
2655
2656 waitable.on_delivery(store, self, event)?;
2657
2658 Ok(Some((event, Some((waitable, handle)))))
2659 }
2660
2661 fn handle_callback_code(
2667 self,
2668 store: &mut StoreOpaque,
2669 guest_thread: QualifiedThreadId,
2670 runtime_instance: RuntimeComponentInstanceIndex,
2671 code: u32,
2672 ) -> Result<()> {
2673 let (code, set) = unpack_callback_code(code);
2674
2675 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2676
2677 let state = store.concurrent_state_mut()?;
2678
2679 state.take_next_switch_item()?;
2680
2681 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2682 let set = store
2683 .instance_state(self.runtime_instance(runtime_instance))
2684 .handle_table()
2685 .waitable_set_rep(handle)?;
2686
2687 Ok(TableId::<WaitableSet>::new(set))
2688 };
2689
2690 match code {
2691 callback_code::EXIT => {
2692 log::trace!("implicit thread {guest_thread:?} completed");
2693 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2694 task.exited = true;
2695 task.callback = None;
2696
2697 let runtime_instance = self.runtime_instance(runtime_instance);
2698
2699 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2704
2705 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2706 }
2707 callback_code::YIELD => {
2708 let old = state
2711 .get_mut(guest_thread.thread)?
2712 .wake_on_cancel
2713 .replace(WakeOnCancel::Yielding);
2714 if !old.is_none() {
2715 bail_bug!("thread unexpectedly had wake_on_cancel set");
2716 }
2717
2718 let task = state.get_mut(guest_thread.task)?;
2719 if let Some(event) = task.event {
2724 assert!(matches!(event, Event::None | Event::Cancelled));
2725 } else {
2726 task.event = Some(Event::None);
2727 }
2728 let call = GuestCall {
2729 thread: guest_thread,
2730 kind: GuestCallKind::DeliverEvent {
2731 instance: self,
2732 set: None,
2733 },
2734 };
2735 state.push_low_priority(WorkItem::GuestCall {
2738 instance: self.runtime_instance(runtime_instance),
2739 call,
2740 });
2741 }
2742 callback_code::WAIT => {
2743 let set = get_set(store, set)?;
2744 let state = store.concurrent_state_mut()?;
2745
2746 if state.get_mut(guest_thread.task)?.event.is_some()
2747 || !state.get_mut(set)?.ready.is_empty()
2748 {
2749 state.push_high_priority(WorkItem::GuestCall {
2751 instance: self.runtime_instance(runtime_instance),
2752 call: GuestCall {
2753 thread: guest_thread,
2754 kind: GuestCallKind::DeliverEvent {
2755 instance: self,
2756 set: Some(set),
2757 },
2758 },
2759 });
2760 } else {
2761 let old = state
2769 .get_mut(guest_thread.thread)?
2770 .wake_on_cancel
2771 .replace(WakeOnCancel::Waiting(set));
2772 if !old.is_none() {
2773 bail_bug!("thread unexpectedly had wake_on_cancel set");
2774 }
2775 let old = state
2776 .get_mut(set)?
2777 .waiting
2778 .insert(guest_thread, WaitMode::Callback(self));
2779 if !old.is_none() {
2780 bail_bug!("set's waiting set already had this thread registered");
2781 }
2782 }
2783 }
2784 _ => bail!(Trap::UnsupportedCallbackCode),
2785 }
2786
2787 Ok(())
2788 }
2789
2790 unsafe fn stage_call<T: 'static>(
2797 self,
2798 mut store: StoreContextMut<T>,
2799 guest_thread: QualifiedThreadId,
2800 callee: SendSyncPtr<VMFuncRef>,
2801 param_count: usize,
2802 result_count: usize,
2803 async_: bool,
2804 callback: Option<SendSyncPtr<VMFuncRef>>,
2805 post_return: Option<SendSyncPtr<VMFuncRef>>,
2806 host_caller: bool,
2807 ) -> Result<()> {
2808 unsafe fn make_call<T: 'static>(
2823 store: StoreContextMut<T>,
2824 guest_thread: QualifiedThreadId,
2825 callee: SendSyncPtr<VMFuncRef>,
2826 param_count: usize,
2827 result_count: usize,
2828 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2829 + Send
2830 + Sync
2831 + 'static
2832 + use<T> {
2833 let token = StoreToken::new(store);
2834 move |store: &mut dyn VMStore| {
2835 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2836
2837 store
2838 .concurrent_state_mut()?
2839 .get_mut(guest_thread.thread)?
2840 .state = GuestThreadState::Running;
2841 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2842 let lower = match task.lower_params.take() {
2843 Some(l) => l,
2844 None => bail_bug!("lower_params missing"),
2845 };
2846
2847 lower(store, &mut storage[..param_count])?;
2848
2849 let mut store = token.as_context_mut(store);
2850
2851 unsafe {
2854 crate::Func::call_unchecked_raw(
2855 &mut store,
2856 callee.as_non_null(),
2857 NonNull::new(
2858 &mut storage[..param_count.max(result_count)]
2859 as *mut [MaybeUninit<ValRaw>] as _,
2860 )
2861 .unwrap(),
2862 )?;
2863 }
2864
2865 Ok(storage)
2866 }
2867 }
2868
2869 let call = unsafe {
2873 make_call(
2874 store.as_context_mut(),
2875 guest_thread,
2876 callee,
2877 param_count,
2878 result_count,
2879 )
2880 };
2881
2882 let callee_instance = store
2883 .0
2884 .concurrent_state_mut()?
2885 .get_mut(guest_thread.task)?
2886 .instance;
2887
2888 let fun = if callback.is_some() {
2889 assert!(async_);
2890
2891 Box::new(move |store: &mut dyn VMStore| {
2892 self.add_guest_thread_to_instance_table(
2893 guest_thread.thread,
2894 store,
2895 callee_instance.index,
2896 )?;
2897 let old_thread = store.set_thread(guest_thread)?;
2898 log::trace!(
2899 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2900 );
2901
2902 store.enter_instance(callee_instance);
2903
2904 let storage = call(store)?;
2911
2912 store.exit_instance(callee_instance)?;
2913
2914 store.set_thread(old_thread)?;
2915 let state = store.concurrent_state_mut()?;
2916 if let Some(t) = old_thread.guest() {
2917 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2918 }
2919 log::trace!("stackless call: restored {old_thread:?} as current thread");
2920
2921 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2924
2925 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2926 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2927 } else {
2928 let token = StoreToken::new(store.as_context_mut());
2929 Box::new(move |store: &mut dyn VMStore| {
2930 self.add_guest_thread_to_instance_table(
2931 guest_thread.thread,
2932 store,
2933 callee_instance.index,
2934 )?;
2935 let old_thread = store.set_thread(guest_thread)?;
2936 log::trace!(
2937 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2938 );
2939 let flags = self.id().get(store).instance_flags(callee_instance.index);
2940
2941 let callee_async_typed = store
2942 .concurrent_state_mut()?
2943 .get_mut(guest_thread.task)?
2944 .async_typed;
2945
2946 if !async_ && callee_async_typed {
2950 store.enter_instance(callee_instance);
2951 }
2952
2953 if !callee_async_typed {
2954 store.enter_sync_call(callee_instance)?;
2955 }
2956
2957 let storage = call(store)?;
2964
2965 if !callee_async_typed {
2966 store.exit_sync_call(callee_instance)?;
2967 }
2968
2969 if !async_ {
2970 if callee_async_typed {
2976 store.exit_instance(callee_instance)?;
2977 }
2978
2979 let lift = {
2980 let state = store.concurrent_state_mut()?;
2981 if !state.get_mut(guest_thread.task)?.result.is_none() {
2982 bail_bug!("task has already produced a result");
2983 }
2984
2985 match state.get_mut(guest_thread.task)?.lift_result.take() {
2986 Some(lift) => lift,
2987 None => bail_bug!("lift_result field is missing"),
2988 }
2989 };
2990
2991 let result = (lift.lift)(store, unsafe {
2994 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2995 &storage[..result_count],
2996 )
2997 })?;
2998
2999 let post_return_arg = match result_count {
3000 0 => ValRaw::i32(0),
3001 1 => unsafe { storage[0].assume_init() },
3004 _ => unreachable!(),
3005 };
3006
3007 unsafe {
3008 call_post_return(
3009 token.as_context_mut(store),
3010 post_return.map(|v| v.as_non_null()),
3011 post_return_arg,
3012 flags,
3013 )?;
3014 }
3015
3016 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
3017 }
3018
3019 store.set_thread(old_thread)?;
3020
3021 store
3022 .concurrent_state_mut()?
3023 .get_mut(guest_thread.task)?
3024 .exited = true;
3025
3026 log::trace!(
3027 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
3028 );
3029
3030 if callee_async_typed {
3031 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
3036 }
3037
3038 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
3040 Ok(())
3041 })
3042 };
3043
3044 store.0.concurrent_state_mut()?.push_work_item(
3045 WorkItem::GuestCall {
3046 instance: callee_instance,
3047 call: GuestCall {
3048 thread: guest_thread,
3049 kind: GuestCallKind::StartImplicit(fun),
3050 },
3051 },
3052 if host_caller {
3053 Priority::High
3054 } else {
3055 Priority::Switch
3056 },
3057 )?;
3058
3059 Ok(())
3060 }
3061
3062 unsafe fn prepare_call<T: 'static>(
3075 self,
3076 mut store: StoreContextMut<T>,
3077 start: NonNull<VMFuncRef>,
3078 return_: NonNull<VMFuncRef>,
3079 caller_instance: RuntimeComponentInstanceIndex,
3080 callee_instance: RuntimeComponentInstanceIndex,
3081 task_return_type: TypeTupleIndex,
3082 callee_async_typed: bool,
3083 memory: *mut VMMemoryDefinition,
3084 string_encoding: StringEncoding,
3085 caller_info: CallerInfo,
3086 ) -> Result<()> {
3087 enum ResultInfo {
3088 Heap { results: u32 },
3089 Stack { result_count: u32 },
3090 }
3091
3092 let result_info = match &caller_info {
3093 CallerInfo::Async {
3094 has_result: true,
3095 params,
3096 } => ResultInfo::Heap {
3097 results: match params.last() {
3098 Some(r) => r.get_u32(),
3099 None => bail_bug!("retptr missing"),
3100 },
3101 },
3102 CallerInfo::Async {
3103 has_result: false, ..
3104 } => ResultInfo::Stack { result_count: 0 },
3105 CallerInfo::Sync {
3106 result_count,
3107 params,
3108 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3109 results: match params.last() {
3110 Some(r) => r.get_u32(),
3111 None => bail_bug!("arg ptr missing"),
3112 },
3113 },
3114 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3115 result_count: *result_count,
3116 },
3117 };
3118
3119 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3120
3121 let start = SendSyncPtr::new(start);
3125 let return_ = SendSyncPtr::new(return_);
3126 let token = StoreToken::new(store.as_context_mut());
3127 let old_thread = store.0.current_guest_thread()?;
3128 let state = store.0.concurrent_state_mut()?;
3129
3130 debug_assert_eq!(
3131 state.get_mut(old_thread.task)?.instance,
3132 self.runtime_instance(caller_instance)
3133 );
3134
3135 let guest_thread = GuestTask::new(
3136 state,
3137 Box::new(move |store, dst| {
3138 let mut store = token.as_context_mut(store);
3139 assert!(dst.len() <= MAX_FLAT_PARAMS);
3140 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3142 let count = match caller_info {
3143 CallerInfo::Async { params, has_result } => {
3147 let params = ¶ms[..params.len() - usize::from(has_result)];
3148 for (param, src) in params.iter().zip(&mut src) {
3149 src.write(*param);
3150 }
3151 params.len()
3152 }
3153
3154 CallerInfo::Sync { params, .. } => {
3156 for (param, src) in params.iter().zip(&mut src) {
3157 src.write(*param);
3158 }
3159 params.len()
3160 }
3161 };
3162 unsafe {
3169 crate::Func::call_unchecked_raw(
3170 &mut store,
3171 start.as_non_null(),
3172 NonNull::new(
3173 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3174 )
3175 .unwrap(),
3176 )?;
3177 }
3178 dst.copy_from_slice(&src[..dst.len()]);
3179 let task = store.0.current_guest_thread()?.task;
3180 let state = store.0.concurrent_state_mut()?;
3181 Waitable::Guest(task).set_event(
3182 state,
3183 Some(Event::Subtask {
3184 status: Status::Started,
3185 }),
3186 )?;
3187 Ok(())
3188 }),
3189 LiftResult {
3190 lift: Box::new(move |store, src| {
3191 let mut store = token.as_context_mut(store);
3194 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3196 my_src.push(ValRaw::u32(*results));
3197 }
3198
3199 unsafe {
3206 crate::Func::call_unchecked_raw(
3207 &mut store,
3208 return_.as_non_null(),
3209 my_src.as_mut_slice().into(),
3210 )?;
3211 }
3212
3213 let thread = store.0.current_guest_thread()?;
3214 let state = store.0.concurrent_state_mut()?;
3215 if sync_caller {
3216 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3217 if let ResultInfo::Stack { result_count } = &result_info {
3218 match result_count {
3219 0 => None,
3220 1 => Some(my_src[0]),
3221 _ => unreachable!(),
3222 }
3223 } else {
3224 None
3225 },
3226 );
3227 }
3228 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3229 }),
3230 ty: task_return_type,
3231 memory: NonNull::new(memory).map(SendSyncPtr::new),
3232 string_encoding,
3233 },
3234 Caller::Guest { thread: old_thread },
3235 None,
3236 self.runtime_instance(callee_instance),
3237 callee_async_typed,
3238 false,
3241 )?;
3242
3243 store.0.set_thread(guest_thread)?;
3246 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3247
3248 Ok(())
3249 }
3250
3251 unsafe fn call_callback<T>(
3256 self,
3257 mut store: StoreContextMut<T>,
3258 function: SendSyncPtr<VMFuncRef>,
3259 event: Event,
3260 handle: u32,
3261 ) -> Result<u32> {
3262 let (ordinal, result) = event.parts();
3263 let params = &mut [
3264 ValRaw::u32(ordinal),
3265 ValRaw::u32(handle),
3266 ValRaw::u32(result),
3267 ];
3268 unsafe {
3273 crate::Func::call_unchecked_raw(
3274 &mut store,
3275 function.as_non_null(),
3276 params.as_mut_slice().into(),
3277 )?;
3278 }
3279 Ok(params[0].get_u32())
3280 }
3281
3282 unsafe fn start_call<T: 'static>(
3295 self,
3296 mut store: StoreContextMut<T>,
3297 callback: *mut VMFuncRef,
3298 post_return: *mut VMFuncRef,
3299 callee: NonNull<VMFuncRef>,
3300 param_count: u32,
3301 result_count: u32,
3302 flags: u32,
3303 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3304 ) -> Result<u32> {
3305 let token = StoreToken::new(store.as_context_mut());
3306 let async_caller = storage.is_none();
3307 let guest_thread = store.0.current_guest_thread()?;
3308 let state = store.0.concurrent_state_mut()?;
3309
3310 if !state.event_loop_running {
3311 bail_bug!("Instance::start_call called without a running event loop");
3312 }
3313
3314 let callee = SendSyncPtr::new(callee);
3315 let param_count = usize::try_from(param_count)?;
3316 assert!(param_count <= MAX_FLAT_PARAMS);
3317 let result_count = usize::try_from(result_count)?;
3318 assert!(result_count <= MAX_FLAT_RESULTS);
3319
3320 let task = state.get_mut(guest_thread.task)?;
3321 let callee_async_typed = task.async_typed;
3322 let callee_instance = task.instance;
3323
3324 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3325
3326 if let Some(callback) = NonNull::new(callback) {
3327 let callback = SendSyncPtr::new(callback);
3331 task.callback = Some(Box::new(move |store, event, handle| {
3332 let store = token.as_context_mut(store);
3333 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3334 }));
3335 }
3336
3337 let Caller::Guest { thread: caller } = &task.caller else {
3338 bail_bug!("start_call unexpectedly invoked for host->guest call");
3341 };
3342 let caller = *caller;
3343 let caller_instance = state.get_mut(caller.task)?.instance;
3344
3345 unsafe {
3347 self.stage_call(
3348 store.as_context_mut(),
3349 guest_thread,
3350 callee,
3351 param_count,
3352 result_count,
3353 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3354 NonNull::new(callback).map(SendSyncPtr::new),
3355 NonNull::new(post_return).map(SendSyncPtr::new),
3356 false,
3357 )?;
3358 }
3359
3360 let old_do_not_suspend = if callee_async_typed {
3361 let state = store.0.instance_state(callee_instance).concurrent_state();
3368 let old_do_not_suspend = state.do_not_suspend;
3369 state.do_not_suspend = false;
3370 Some(old_do_not_suspend)
3371 } else {
3372 None
3373 };
3374
3375 let state = store.0.concurrent_state_mut()?;
3376
3377 let guest_waitable = Waitable::Guest(guest_thread.task);
3380 let old_set = guest_waitable.common(state)?.set;
3381 let set = state.get_mut(caller.thread)?.sync_call_set;
3382 guest_waitable.join(state, Some(set))?;
3383
3384 store.0.set_thread(CurrentThread::None)?;
3385
3386 let mut yielded = false;
3402 let (status, waitable) = loop {
3403 store.0.suspend(if yielded {
3404 SuspendReason::Waiting {
3405 set,
3406 thread: caller,
3407 }
3408 } else {
3409 yielded = true;
3410 SuspendReason::YieldingToSubtask { thread: caller }
3411 })?;
3412
3413 if let Some(old_do_not_suspend) = old_do_not_suspend {
3414 store
3415 .0
3416 .instance_state(callee_instance)
3417 .concurrent_state()
3418 .do_not_suspend = old_do_not_suspend;
3419 }
3420
3421 let state = store.0.concurrent_state_mut()?;
3422
3423 log::trace!("taking event for {:?}", guest_thread.task);
3424 let event = guest_waitable.take_event(state)?;
3425 let Some(Event::Subtask { status }) = event else {
3426 bail_bug!("subtasks should only get subtask events, got {event:?}")
3427 };
3428
3429 log::trace!("status {status:?} for {:?}", guest_thread.task);
3430
3431 if status == Status::Returned {
3432 break (status, None);
3434 } else if async_caller {
3435 let handle = store
3439 .0
3440 .instance_state(caller_instance)
3441 .handle_table()
3442 .subtask_insert_guest(guest_thread.task.rep())?;
3443 store
3444 .0
3445 .concurrent_state_mut()?
3446 .get_mut(guest_thread.task)?
3447 .common
3448 .handle = Some(handle);
3449 break (status, Some(handle));
3450 } else {
3451 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3455 }
3456 };
3457
3458 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3459
3460 store.0.set_thread(caller)?;
3462 store
3463 .0
3464 .concurrent_state_mut()?
3465 .get_mut(caller.thread)?
3466 .state = GuestThreadState::Running;
3467 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3468
3469 if let Some(storage) = storage {
3470 let state = store.0.concurrent_state_mut()?;
3474 let task = state.get_mut(guest_thread.task)?;
3475 if let Some(result) = task.sync_result.take()? {
3476 if let Some(result) = result {
3477 storage[0] = MaybeUninit::new(result);
3478 }
3479
3480 if task.exited && task.ready_to_delete() {
3481 Waitable::Guest(guest_thread.task).delete_from(state)?;
3482 }
3483 }
3484 }
3485
3486 Ok(status.pack(waitable))
3487 }
3488
3489 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3505 self,
3506 mut store: StoreContextMut<'_, T>,
3507 host_task: EnteredHostTask,
3508 future: impl Future<Output = Result<R>> + Send + 'static,
3509 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool, Option<TableId<HostTask>>) -> Result<()>
3510 + Send
3511 + 'static,
3512 ) -> Result<u32> {
3513 let token = StoreToken::new(store.as_context_mut());
3514
3515 let (join_handle, future) = JoinHandle::run(future);
3518 let mut future = Box::pin(future);
3519
3520 let poll = tls::set(store.0, || {
3525 future
3526 .as_mut()
3527 .poll(&mut Context::from_waker(&Waker::noop()))
3528 });
3529
3530 match poll {
3531 Poll::Ready(result) => {
3533 let result = result.transpose()?;
3534 let task = store.0.current_materialized_host_task()?;
3537 lower(store.as_context_mut(), result, true, task)?;
3538 return Ok(Status::Returned.pack(None));
3539 }
3540
3541 Poll::Pending => {}
3543 }
3544
3545 let Some(task) = store.0.materialize_host_task_id()? else {
3549 bail_bug!("current thread is not a host thread")
3550 };
3551 {
3552 let state = &mut store.0.concurrent_state_mut()?.get_mut(task)?.state;
3553 assert!(matches!(state, HostTaskState::CalleeStarted));
3554 *state = HostTaskState::CalleeRunning(join_handle);
3555 }
3556
3557 let future = Box::pin(async move {
3565 let result = match run_with_host_task_set(task, future).await? {
3566 Some(result) => Some(result?),
3567 None => None,
3568 };
3569 let on_complete = move |store: &mut dyn VMStore| {
3570 let mut store = token.as_context_mut(store);
3574 let old = store.0.set_thread(task)?;
3575
3576 let status = if result.is_some() {
3577 Status::Returned
3578 } else {
3579 Status::ReturnCancelled
3580 };
3581
3582 lower(store.as_context_mut(), result, false, Some(task))?;
3583 let state = store.0.concurrent_state_mut()?;
3584 match &mut state.get_mut(task)?.state {
3585 HostTaskState::CalleeDone { .. } => {}
3588
3589 other => *other = HostTaskState::CalleeDone { cancelled: false },
3591 }
3592 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3593
3594 store.0.set_thread(old)?;
3595 Ok(())
3596 };
3597
3598 tls::get(move |store| {
3603 store
3604 .concurrent_state_mut()?
3605 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3606 on_complete,
3607 ))));
3608 Ok(())
3609 })
3610 });
3611
3612 let caller = match host_task {
3615 Some(caller) => caller,
3616 None => bail_bug!("host task wasn't created but should have been"),
3617 };
3618 let state = store.0.concurrent_state_mut()?;
3619 state.push_future(future);
3620 let instance = state.get_mut(caller.task)?.instance;
3621 let handle = store
3622 .0
3623 .instance_state(instance)
3624 .handle_table()
3625 .subtask_insert_host(task.rep())?;
3626 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3627 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3628
3629 store.0.set_thread(caller)?;
3633 Ok(Status::Started.pack(Some(handle)))
3634 }
3635
3636 pub(crate) fn task_return(
3639 self,
3640 store: &mut dyn VMStore,
3641 ty: TypeTupleIndex,
3642 options: OptionsIndex,
3643 storage: &[ValRaw],
3644 ) -> Result<()> {
3645 let guest_thread = store.current_guest_thread()?;
3646 let state = store.concurrent_state_mut()?;
3647 let lift = state
3648 .get_mut(guest_thread.task)?
3649 .lift_result
3650 .take()
3651 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3652 if !state.get_mut(guest_thread.task)?.result.is_none() {
3653 bail_bug!("task result unexpectedly already set");
3654 }
3655
3656 let CanonicalOptions {
3657 string_encoding,
3658 data_model,
3659 ..
3660 } = &self.id().get(store).component().env_component().options[options];
3661
3662 let invalid = ty != lift.ty
3663 || string_encoding != &lift.string_encoding
3664 || match data_model {
3665 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3666 Some(memory) => {
3667 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3668 let actual = self.id().get(store).runtime_memory(memory);
3669 expected != actual.as_ptr()
3670 }
3671 None => false,
3674 },
3675 CanonicalOptionsDataModel::Gc { .. } => true,
3677 };
3678
3679 if invalid {
3680 bail!(Trap::TaskReturnInvalid);
3681 }
3682
3683 log::trace!("task.return for {guest_thread:?}");
3684
3685 let result = (lift.lift)(store, storage)?;
3686 self.task_complete(store, guest_thread.task, result, Status::Returned)
3687 }
3688
3689 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3691 let guest_thread = store.current_guest_thread()?;
3692 let state = store.concurrent_state_mut()?;
3693 let task = state.get_mut(guest_thread.task)?;
3694 if !task.cancel_request_delivered {
3695 bail!(Trap::TaskCancelNotCancelled);
3696 }
3697 _ = task
3698 .lift_result
3699 .take()
3700 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3701
3702 if !task.result.is_none() {
3703 bail_bug!("task result should not bet set yet");
3704 }
3705
3706 log::trace!("task.cancel for {guest_thread:?}");
3707
3708 self.task_complete(
3709 store,
3710 guest_thread.task,
3711 Box::new(DummyResult),
3712 Status::ReturnCancelled,
3713 )
3714 }
3715
3716 fn task_complete(
3722 self,
3723 store: &mut StoreOpaque,
3724 guest_task: TableId<GuestTask>,
3725 result: Box<dyn Any + Send + Sync>,
3726 status: Status,
3727 ) -> Result<()> {
3728 store
3729 .component_resource_tables(Some(self))?
3730 .validate_scope_exit()?;
3731
3732 let state = store.concurrent_state_mut()?;
3733 let task = state.get_mut(guest_task)?;
3734
3735 if let Caller::Host { tx, .. } = &mut task.caller {
3736 if let Some(tx) = tx.take() {
3737 _ = tx.send(result);
3738 }
3739 } else {
3740 task.result = Some(result);
3741 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3742 }
3743
3744 Ok(())
3745 }
3746
3747 pub(crate) fn waitable_set_new(
3749 self,
3750 store: &mut StoreOpaque,
3751 caller_instance: RuntimeComponentInstanceIndex,
3752 ) -> Result<u32> {
3753 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3754 let handle = store
3755 .instance_state(self.runtime_instance(caller_instance))
3756 .handle_table()
3757 .waitable_set_insert(set.rep())?;
3758 log::trace!("new waitable set {set:?} (handle {handle})");
3759 Ok(handle)
3760 }
3761
3762 pub(crate) fn waitable_set_drop(
3764 self,
3765 store: &mut StoreOpaque,
3766 caller_instance: RuntimeComponentInstanceIndex,
3767 set: u32,
3768 ) -> Result<()> {
3769 let rep = store
3770 .instance_state(self.runtime_instance(caller_instance))
3771 .handle_table()
3772 .waitable_set_remove(set)?;
3773
3774 log::trace!("drop waitable set {rep} (handle {set})");
3775
3776 if !store
3780 .concurrent_state_mut()?
3781 .get_mut(TableId::<WaitableSet>::new(rep))?
3782 .waiting
3783 .is_empty()
3784 {
3785 bail!(Trap::WaitableSetDropHasWaiters);
3786 }
3787
3788 store
3789 .concurrent_state_mut()?
3790 .delete(TableId::<WaitableSet>::new(rep))?;
3791
3792 Ok(())
3793 }
3794
3795 pub(crate) fn waitable_join(
3797 self,
3798 store: &mut StoreOpaque,
3799 caller_instance: RuntimeComponentInstanceIndex,
3800 waitable_handle: u32,
3801 set_handle: u32,
3802 ) -> Result<()> {
3803 let mut instance = self.id().get_mut(store);
3804 let waitable =
3805 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3806
3807 let set = if set_handle == 0 {
3808 None
3809 } else {
3810 let set = instance.instance_states().0[caller_instance]
3811 .handle_table()
3812 .waitable_set_rep(set_handle)?;
3813
3814 let state = store.concurrent_state_mut()?;
3815 if let Some(old) = waitable.common(state)?.set
3816 && state.get_mut(old)?.is_sync_call_set
3817 {
3818 bail!(Trap::WaitableSyncAndAsync);
3819 }
3820
3821 Some(TableId::<WaitableSet>::new(set))
3822 };
3823
3824 log::trace!(
3825 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3826 );
3827
3828 waitable.join(store.concurrent_state_mut()?, set)
3829 }
3830
3831 pub(crate) fn subtask_drop(
3833 self,
3834 store: &mut StoreOpaque,
3835 caller_instance: RuntimeComponentInstanceIndex,
3836 task_id: u32,
3837 ) -> Result<()> {
3838 self.waitable_join(store, caller_instance, task_id, 0)?;
3839
3840 let (rep, is_host) = store
3841 .instance_state(self.runtime_instance(caller_instance))
3842 .handle_table()
3843 .subtask_remove(task_id)?;
3844
3845 let concurrent_state = store.concurrent_state_mut()?;
3846 let (waitable, delete) = if is_host {
3847 let id = TableId::<HostTask>::new(rep);
3848 let task = concurrent_state.get_mut(id)?;
3849 match &task.state {
3850 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3851 HostTaskState::CalleeDone { .. } => {}
3852 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3853 bail_bug!("invalid state for callee in `subtask.drop`")
3854 }
3855 }
3856 (Waitable::Host(id), true)
3857 } else {
3858 let id = TableId::<GuestTask>::new(rep);
3859 let task = concurrent_state.get_mut(id)?;
3860 if task.lift_result.is_some() {
3861 bail!(Trap::SubtaskDropNotResolved);
3862 }
3863 (
3864 Waitable::Guest(id),
3865 concurrent_state.get_mut(id)?.ready_to_delete(),
3866 )
3867 };
3868
3869 waitable.common(concurrent_state)?.handle = None;
3870
3871 if waitable.take_event(concurrent_state)?.is_some() {
3874 bail!(Trap::SubtaskDropNotResolved);
3875 }
3876
3877 if delete {
3878 waitable.delete_from(concurrent_state)?;
3879 }
3880
3881 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3882 Ok(())
3883 }
3884
3885 pub(crate) fn waitable_set_wait(
3887 self,
3888 store: &mut StoreOpaque,
3889 options: OptionsIndex,
3890 set: u32,
3891 payload: u32,
3892 ) -> Result<u32> {
3893 let &CanonicalOptions {
3894 instance: caller_instance,
3895 ..
3896 } = &self.id().get(store).component().env_component().options[options];
3897 let caller = self.runtime_instance(caller_instance);
3898 let rep = store
3899 .instance_state(self.runtime_instance(caller_instance))
3900 .handle_table()
3901 .waitable_set_rep(set)?;
3902
3903 self.waitable_check(
3904 store,
3905 caller,
3906 WaitableCheck::Wait,
3907 WaitableCheckParams {
3908 set: TableId::new(rep),
3909 options,
3910 payload,
3911 },
3912 )
3913 }
3914
3915 pub(crate) fn waitable_set_poll(
3917 self,
3918 store: &mut StoreOpaque,
3919 options: OptionsIndex,
3920 set: u32,
3921 payload: u32,
3922 ) -> Result<u32> {
3923 let &CanonicalOptions {
3924 instance: caller_instance,
3925 ..
3926 } = &self.id().get(store).component().env_component().options[options];
3927 let caller = self.runtime_instance(caller_instance);
3928 let rep = store
3929 .instance_state(caller)
3930 .handle_table()
3931 .waitable_set_rep(set)?;
3932
3933 self.waitable_check(
3934 store,
3935 caller,
3936 WaitableCheck::Poll,
3937 WaitableCheckParams {
3938 set: TableId::new(rep),
3939 options,
3940 payload,
3941 },
3942 )
3943 }
3944
3945 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3947 let thread_id = store.current_guest_thread()?.thread;
3948 match store
3949 .concurrent_state_mut()?
3950 .get_mut(thread_id)?
3951 .instance_rep
3952 {
3953 Some(r) => Ok(r),
3954 None => bail_bug!("thread should have instance_rep by now"),
3955 }
3956 }
3957
3958 pub(crate) fn thread_new_indirect<T: 'static>(
3960 self,
3961 mut store: StoreContextMut<T>,
3962 runtime_instance: RuntimeComponentInstanceIndex,
3963 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3965 start_func_idx: u32,
3966 context: i32,
3967 ) -> Result<u32> {
3968 log::trace!("creating new thread");
3969
3970 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3971 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3972 let callee = instance
3973 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3974 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3975 if callee.type_index(store.0) != start_func_ty.type_index() {
3976 bail!(Trap::ThreadNewIndirectInvalidType);
3977 }
3978
3979 let token = StoreToken::new(store.as_context_mut());
3980 let start_func = Box::new(
3981 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3982 let old_thread = store.set_thread(guest_thread)?;
3983 log::trace!(
3984 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3985 );
3986
3987 let mut store = token.as_context_mut(store);
3988 let mut params = [ValRaw::i32(context)];
3989 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3992
3993 store.0.set_thread(old_thread)?;
3994
3995 let runtime_instance = self.runtime_instance(runtime_instance);
3996
3997 store
4000 .0
4001 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
4002
4003 store
4004 .0
4005 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
4006
4007 log::trace!("explicit thread {guest_thread:?} completed");
4008 let state = store.0.concurrent_state_mut()?;
4009 if let Some(t) = old_thread.guest() {
4010 state.get_mut(t.thread)?.state = GuestThreadState::Running;
4011 }
4012 log::trace!("thread start: restored {old_thread:?} as current thread");
4013
4014 Ok(())
4015 },
4016 );
4017
4018 let current_thread = store.0.current_guest_thread()?;
4019 let state = store.0.concurrent_state_mut()?;
4020 let parent_task = current_thread.task;
4021
4022 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
4023 let thread_id = state.push(new_thread)?;
4024 state.get_mut(parent_task)?.threads.insert(thread_id);
4025
4026 log::trace!("new thread with id {thread_id:?} created");
4027
4028 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
4029 }
4030
4031 pub(crate) fn resume_thread(
4032 self,
4033 store: &mut StoreOpaque,
4034 runtime_instance: RuntimeComponentInstanceIndex,
4035 thread_idx: u32,
4036 how: ResumeThread,
4037 ) -> Result<bool> {
4038 let thread_id =
4039 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
4040 let state = store.concurrent_state_mut()?;
4041 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
4042
4043 if store.current_guest_thread()? == guest_thread {
4044 bail!(Trap::CannotResumeThread);
4045 }
4046
4047 let state = store.concurrent_state_mut()?;
4048 let thread = state.get_mut(guest_thread.thread)?;
4049 let priority = match how {
4050 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
4051 ResumeThread::ResumeLater => Priority::Low,
4052 };
4053
4054 match (&how, &thread.state) {
4055 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
4057 (ResumeThread::Promote, _) => return Ok(false),
4058
4059 (
4062 ResumeThread::Resume | ResumeThread::ResumeLater,
4063 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
4064 ) => {}
4065 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
4066 bail!(Trap::CannotResumeThread)
4067 }
4068 }
4069
4070 match mem::replace(&mut thread.state, GuestThreadState::Running) {
4071 GuestThreadState::NotStartedExplicit(start_func) => {
4072 log::trace!("starting thread {guest_thread:?}");
4073 let guest_call = WorkItem::GuestCall {
4074 instance: self.runtime_instance(runtime_instance),
4075 call: GuestCall {
4076 thread: guest_thread,
4077 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
4078 start_func(store, guest_thread)
4079 })),
4080 },
4081 };
4082 store
4083 .concurrent_state_mut()?
4084 .push_work_item(guest_call, priority)?;
4085 }
4086 GuestThreadState::Suspended(fiber) => {
4087 log::trace!("resuming thread {thread_id:?} that was suspended");
4088 store.concurrent_state_mut()?.push_work_item(
4089 WorkItem::ResumeFiber {
4090 instance: self.runtime_instance(runtime_instance),
4091 thread: guest_thread,
4092 fiber,
4093 },
4094 priority,
4095 )?;
4096 }
4097 GuestThreadState::Ready { fiber } => {
4098 log::trace!("resuming thread {thread_id:?} that was ready");
4099 thread.state = GuestThreadState::Ready { fiber };
4100 store
4101 .concurrent_state_mut()?
4102 .promote_thread_work_item(guest_thread)?;
4103 }
4104 other @ (GuestThreadState::NotStartedImplicit
4105 | GuestThreadState::Running
4106 | GuestThreadState::Completed) => {
4107 thread.state = other;
4108 }
4109 }
4110 Ok(true)
4111 }
4112
4113 fn add_guest_thread_to_instance_table(
4114 self,
4115 thread_id: TableId<GuestThread>,
4116 store: &mut StoreOpaque,
4117 runtime_instance: RuntimeComponentInstanceIndex,
4118 ) -> Result<u32> {
4119 let guest_id = store
4120 .instance_state(self.runtime_instance(runtime_instance))
4121 .thread_handle_table()
4122 .guest_thread_insert(thread_id.rep())?;
4123 store
4124 .concurrent_state_mut()?
4125 .get_mut(thread_id)?
4126 .instance_rep = Some(guest_id);
4127 Ok(guest_id)
4128 }
4129
4130 pub(crate) fn suspension_intrinsic(
4134 self,
4135 store: &mut StoreOpaque,
4136 caller: RuntimeComponentInstanceIndex,
4137 yielding: bool,
4138 to_thread: SuspensionTarget,
4139 ) -> Result<WaitResult> {
4140 let check_suspend = match to_thread {
4141 SuspensionTarget::Promote(thread) => {
4142 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4143 }
4144 SuspensionTarget::Resume(thread) => {
4145 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4146 bail_bug!(
4147 "`resume_thread` should only ever return false \
4148 when `ResumeThread::Promote` is passed to it"
4149 );
4150 }
4151 false
4152 }
4153 SuspensionTarget::None => true,
4154 };
4155
4156 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4157 return if yielding {
4158 Ok(WaitResult::Completed)
4159 } else {
4160 Err(Trap::CannotBlockSyncTask.into())
4161 };
4162 }
4163
4164 let guest_thread = store.current_guest_thread()?;
4165
4166 let reason = if yielding {
4167 SuspendReason::Yielding {
4168 thread: guest_thread,
4169 }
4170 } else {
4171 SuspendReason::ExplicitlySuspending {
4172 thread: guest_thread,
4173 }
4174 };
4175
4176 store.suspend(reason)?;
4177
4178 Ok(WaitResult::Completed)
4179 }
4180
4181 fn waitable_check(
4183 self,
4184 store: &mut StoreOpaque,
4185 caller: RuntimeInstance,
4186 check: WaitableCheck,
4187 params: WaitableCheckParams,
4188 ) -> Result<u32> {
4189 let guest_thread = store.current_guest_thread()?;
4190
4191 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4192
4193 let state = store.concurrent_state_mut()?;
4194 let task = state.get_mut(guest_thread.task)?;
4195
4196 match &check {
4199 WaitableCheck::Wait => {
4200 let set = params.set;
4201
4202 if (task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4203 && state.get_mut(set)?.ready.is_empty()
4204 {
4205 store.switch_or_trap_if_may_not_suspend(caller)?;
4206
4207 store.suspend(SuspendReason::Waiting {
4208 set,
4209 thread: guest_thread,
4210 })?;
4211 }
4212 }
4213 WaitableCheck::Poll => {}
4214 }
4215
4216 log::trace!(
4217 "waitable check for {guest_thread:?}; set {:?}, part two",
4218 params.set
4219 );
4220
4221 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4223
4224 let (ordinal, handle, result) = match &check {
4225 WaitableCheck::Wait => {
4226 let (event, waitable) = match event {
4227 Some(p) => p,
4228 None => bail_bug!("event expected to be present"),
4229 };
4230 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4231 let (ordinal, result) = event.parts();
4232 (ordinal, handle, result)
4233 }
4234 WaitableCheck::Poll => {
4235 if let Some((event, waitable)) = event {
4236 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4237 let (ordinal, result) = event.parts();
4238 (ordinal, handle, result)
4239 } else {
4240 log::trace!(
4241 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4242 guest_thread.task,
4243 params.set
4244 );
4245 let (ordinal, result) = Event::None.parts();
4246 (ordinal, 0, result)
4247 }
4248 }
4249 };
4250 let memory = self.options_memory_mut(store, params.options);
4251 let ptr = crate::component::func::validate_inbounds_dynamic(
4252 &CanonicalAbiInfo::POINTER_PAIR,
4253 memory,
4254 &ValRaw::u32(params.payload),
4255 )?;
4256 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4257 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4258 Ok(ordinal)
4259 }
4260
4261 pub(crate) fn subtask_cancel(
4263 self,
4264 store: &mut StoreOpaque,
4265 caller_instance: RuntimeComponentInstanceIndex,
4266 async_: bool,
4267 task_id: u32,
4268 ) -> Result<u32> {
4269 let (rep, is_host) = store
4270 .instance_state(self.runtime_instance(caller_instance))
4271 .handle_table()
4272 .subtask_rep(task_id)?;
4273 let waitable = if is_host {
4274 Waitable::Host(TableId::<HostTask>::new(rep))
4275 } else {
4276 Waitable::Guest(TableId::<GuestTask>::new(rep))
4277 };
4278 let concurrent_state = store.concurrent_state_mut()?;
4279
4280 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4281
4282 waitable.trap_if_in_waitable_set(concurrent_state)?;
4283
4284 let needs_block;
4285 if let Waitable::Host(host_task) = waitable {
4286 let state = &mut concurrent_state.get_mut(host_task)?.state;
4287 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
4288 HostTaskState::CalleeRunning(handle) => {
4295 handle.abort();
4296 needs_block = true;
4297 }
4298
4299 HostTaskState::CalleeDone { cancelled } => {
4302 if cancelled {
4303 bail!(Trap::SubtaskCancelAfterTerminal);
4304 } else {
4305 needs_block = false;
4308 }
4309 }
4310
4311 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4314 bail_bug!("invalid states for host callee")
4315 }
4316 }
4317 } else {
4318 let guest_task = TableId::<GuestTask>::new(rep);
4319 let task = concurrent_state.get_mut(guest_task)?;
4320 if !task.already_lowered_parameters() {
4321 store.cancel_guest_subtask_without_lowered_parameters(
4322 self.runtime_instance(caller_instance),
4323 guest_task,
4324 )?;
4325 return Ok(Status::StartCancelled as u32);
4326 } else if !task.returned_or_cancelled() {
4327 task.event = Some(Event::Cancelled);
4335 let runtime_instance = task.instance;
4336 for thread in task.threads.clone() {
4337 let thread = QualifiedThreadId {
4338 task: guest_task,
4339 thread,
4340 };
4341 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4342
4343 let yield_ = |store: &mut StoreOpaque| {
4344 let state = store.instance_state(runtime_instance).concurrent_state();
4349 let old_do_not_suspend = state.do_not_suspend;
4350 state.do_not_suspend = false;
4351
4352 let caller = store.current_guest_thread()?;
4353
4354 let state = store.concurrent_state_mut()?;
4359 let set = state.get_mut(caller.thread)?.sync_call_set;
4360 waitable.join(state, Some(set))?;
4361
4362 store.suspend(SuspendReason::YieldingToSubtask { thread: caller })?;
4363
4364 let state = store.concurrent_state_mut()?;
4365 waitable.join(state, None)?;
4366
4367 store
4368 .instance_state(runtime_instance)
4369 .concurrent_state()
4370 .do_not_suspend = old_do_not_suspend;
4371
4372 Ok::<(), crate::Error>(())
4373 };
4374
4375 match thread_mut.wake_on_cancel.take() {
4376 WakeOnCancel::Waiting(set) => {
4377 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread)
4379 {
4380 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4381 instance: runtime_instance,
4382 call: GuestCall {
4383 thread,
4384 kind: GuestCallKind::DeliverEvent {
4385 instance,
4386 set: None,
4387 },
4388 },
4389 },
4390 other => bail_bug!(
4391 "expected `Some(WaitMode::Callback(_))`; got `{other:?}`"
4392 ),
4393 };
4394 concurrent_state.set_switch_item(item)?;
4395
4396 yield_(store)?;
4397
4398 break;
4399 }
4400 WakeOnCancel::Yielding => {
4401 if concurrent_state.promote_thread_work_item(thread)? {
4402 yield_(store)?;
4403 break;
4404 } else {
4405 bail_bug!("thread with `WakeOnCancel::Yielding` not promotable");
4406 }
4407 }
4408 WakeOnCancel::None => {}
4409 }
4410 }
4411
4412 needs_block = !store
4415 .concurrent_state_mut()?
4416 .get_mut(guest_task)?
4417 .returned_or_cancelled()
4418 } else {
4419 needs_block = false;
4420 }
4421 };
4422
4423 if needs_block {
4427 if async_ {
4428 return Ok(BLOCKED);
4429 }
4430
4431 let old_next_switch_item = {
4434 let state = store.concurrent_state_mut()?;
4435 let item = state.next_switch_item.take();
4436 state.push(item)?
4440 };
4441
4442 store.wait_for_event(self.runtime_instance(caller_instance), waitable)?;
4445
4446 let state = store.concurrent_state_mut()?;
4447 state.next_switch_item = state.delete(old_next_switch_item)?;
4448
4449 }
4451
4452 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4453 if let Some(Event::Subtask {
4454 status: status @ (Status::Returned | Status::ReturnCancelled),
4455 }) = event
4456 {
4457 Ok(status as u32)
4458 } else {
4459 bail!(Trap::SubtaskCancelAfterTerminal);
4460 }
4461 }
4462}
4463
4464pub trait VMComponentAsyncStore {
4472 unsafe fn prepare_call(
4478 &mut self,
4479 instance: Instance,
4480 memory: *mut VMMemoryDefinition,
4481 start: NonNull<VMFuncRef>,
4482 return_: NonNull<VMFuncRef>,
4483 caller_instance: RuntimeComponentInstanceIndex,
4484 callee_instance: RuntimeComponentInstanceIndex,
4485 task_return_type: TypeTupleIndex,
4486 callee_async: bool,
4487 string_encoding: StringEncoding,
4488 result_count: u32,
4489 storage: *mut ValRaw,
4490 storage_len: usize,
4491 ) -> Result<()>;
4492
4493 unsafe fn sync_start(
4496 &mut self,
4497 instance: Instance,
4498 callback: *mut VMFuncRef,
4499 callee: NonNull<VMFuncRef>,
4500 param_count: u32,
4501 storage: *mut MaybeUninit<ValRaw>,
4502 storage_len: usize,
4503 ) -> Result<()>;
4504
4505 unsafe fn async_start(
4508 &mut self,
4509 instance: Instance,
4510 callback: *mut VMFuncRef,
4511 post_return: *mut VMFuncRef,
4512 callee: NonNull<VMFuncRef>,
4513 param_count: u32,
4514 result_count: u32,
4515 flags: u32,
4516 ) -> Result<u32>;
4517
4518 fn future_write(
4520 &mut self,
4521 instance: Instance,
4522 caller: RuntimeComponentInstanceIndex,
4523 ty: TypeFutureTableIndex,
4524 options: OptionsIndex,
4525 future: u32,
4526 address: u32,
4527 ) -> Result<u32>;
4528
4529 fn future_read(
4531 &mut self,
4532 instance: Instance,
4533 caller: RuntimeComponentInstanceIndex,
4534 ty: TypeFutureTableIndex,
4535 options: OptionsIndex,
4536 future: u32,
4537 address: u32,
4538 ) -> Result<u32>;
4539
4540 fn future_drop_writable(
4542 &mut self,
4543 instance: Instance,
4544 ty: TypeFutureTableIndex,
4545 writer: u32,
4546 ) -> Result<()>;
4547
4548 fn stream_write(
4550 &mut self,
4551 instance: Instance,
4552 caller: RuntimeComponentInstanceIndex,
4553 ty: TypeStreamTableIndex,
4554 options: OptionsIndex,
4555 stream: u32,
4556 address: u32,
4557 count: u32,
4558 ) -> Result<u32>;
4559
4560 fn stream_read(
4562 &mut self,
4563 instance: Instance,
4564 caller: RuntimeComponentInstanceIndex,
4565 ty: TypeStreamTableIndex,
4566 options: OptionsIndex,
4567 stream: u32,
4568 address: u32,
4569 count: u32,
4570 ) -> Result<u32>;
4571
4572 fn flat_stream_write(
4575 &mut self,
4576 instance: Instance,
4577 caller: RuntimeComponentInstanceIndex,
4578 ty: TypeStreamTableIndex,
4579 options: OptionsIndex,
4580 payload_size: u32,
4581 payload_align: u32,
4582 stream: u32,
4583 address: u32,
4584 count: u32,
4585 ) -> Result<u32>;
4586
4587 fn flat_stream_read(
4590 &mut self,
4591 instance: Instance,
4592 caller: RuntimeComponentInstanceIndex,
4593 ty: TypeStreamTableIndex,
4594 options: OptionsIndex,
4595 payload_size: u32,
4596 payload_align: u32,
4597 stream: u32,
4598 address: u32,
4599 count: u32,
4600 ) -> Result<u32>;
4601
4602 fn stream_drop_writable(
4604 &mut self,
4605 instance: Instance,
4606 ty: TypeStreamTableIndex,
4607 writer: u32,
4608 ) -> Result<()>;
4609
4610 fn error_context_debug_message(
4612 &mut self,
4613 instance: Instance,
4614 ty: TypeComponentLocalErrorContextTableIndex,
4615 options: OptionsIndex,
4616 err_ctx_handle: u32,
4617 debug_msg_address: u32,
4618 ) -> Result<()>;
4619
4620 fn thread_new_indirect(
4622 &mut self,
4623 instance: Instance,
4624 caller: RuntimeComponentInstanceIndex,
4625 func_ty_idx: TypeFuncIndex,
4626 start_func_table_idx: RuntimeTableIndex,
4627 start_func_idx: u32,
4628 context: i32,
4629 ) -> Result<u32>;
4630}
4631
4632impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4634 unsafe fn prepare_call(
4635 &mut self,
4636 instance: Instance,
4637 memory: *mut VMMemoryDefinition,
4638 start: NonNull<VMFuncRef>,
4639 return_: NonNull<VMFuncRef>,
4640 caller_instance: RuntimeComponentInstanceIndex,
4641 callee_instance: RuntimeComponentInstanceIndex,
4642 task_return_type: TypeTupleIndex,
4643 callee_async: bool,
4644 string_encoding: StringEncoding,
4645 result_count_or_max_if_async: u32,
4646 storage: *mut ValRaw,
4647 storage_len: usize,
4648 ) -> Result<()> {
4649 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4653
4654 unsafe {
4655 instance.prepare_call(
4656 StoreContextMut(self),
4657 start,
4658 return_,
4659 caller_instance,
4660 callee_instance,
4661 task_return_type,
4662 callee_async,
4663 memory,
4664 string_encoding,
4665 match result_count_or_max_if_async {
4666 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4667 params,
4668 has_result: false,
4669 },
4670 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4671 params,
4672 has_result: true,
4673 },
4674 result_count => CallerInfo::Sync {
4675 params,
4676 result_count,
4677 },
4678 },
4679 )
4680 }
4681 }
4682
4683 unsafe fn sync_start(
4684 &mut self,
4685 instance: Instance,
4686 callback: *mut VMFuncRef,
4687 callee: NonNull<VMFuncRef>,
4688 param_count: u32,
4689 storage: *mut MaybeUninit<ValRaw>,
4690 storage_len: usize,
4691 ) -> Result<()> {
4692 unsafe {
4693 instance
4694 .start_call(
4695 StoreContextMut(self),
4696 callback,
4697 ptr::null_mut(),
4698 callee,
4699 param_count,
4700 1,
4701 START_FLAG_ASYNC_CALLEE,
4702 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4706 )
4707 .map(drop)
4708 }
4709 }
4710
4711 unsafe fn async_start(
4712 &mut self,
4713 instance: Instance,
4714 callback: *mut VMFuncRef,
4715 post_return: *mut VMFuncRef,
4716 callee: NonNull<VMFuncRef>,
4717 param_count: u32,
4718 result_count: u32,
4719 flags: u32,
4720 ) -> Result<u32> {
4721 unsafe {
4722 instance.start_call(
4723 StoreContextMut(self),
4724 callback,
4725 post_return,
4726 callee,
4727 param_count,
4728 result_count,
4729 flags,
4730 None,
4731 )
4732 }
4733 }
4734
4735 fn future_write(
4736 &mut self,
4737 instance: Instance,
4738 caller: RuntimeComponentInstanceIndex,
4739 ty: TypeFutureTableIndex,
4740 options: OptionsIndex,
4741 future: u32,
4742 address: u32,
4743 ) -> Result<u32> {
4744 instance
4745 .guest_write(
4746 StoreContextMut(self),
4747 caller,
4748 TransmitIndex::Future(ty),
4749 options,
4750 None,
4751 future,
4752 address,
4753 1,
4754 )
4755 .map(|result| result.encode())
4756 }
4757
4758 fn future_read(
4759 &mut self,
4760 instance: Instance,
4761 caller: RuntimeComponentInstanceIndex,
4762 ty: TypeFutureTableIndex,
4763 options: OptionsIndex,
4764 future: u32,
4765 address: u32,
4766 ) -> Result<u32> {
4767 instance
4768 .guest_read(
4769 StoreContextMut(self),
4770 caller,
4771 TransmitIndex::Future(ty),
4772 options,
4773 None,
4774 future,
4775 address,
4776 1,
4777 )
4778 .map(|result| result.encode())
4779 }
4780
4781 fn stream_write(
4782 &mut self,
4783 instance: Instance,
4784 caller: RuntimeComponentInstanceIndex,
4785 ty: TypeStreamTableIndex,
4786 options: OptionsIndex,
4787 stream: u32,
4788 address: u32,
4789 count: u32,
4790 ) -> Result<u32> {
4791 instance
4792 .guest_write(
4793 StoreContextMut(self),
4794 caller,
4795 TransmitIndex::Stream(ty),
4796 options,
4797 None,
4798 stream,
4799 address,
4800 count,
4801 )
4802 .map(|result| result.encode())
4803 }
4804
4805 fn stream_read(
4806 &mut self,
4807 instance: Instance,
4808 caller: RuntimeComponentInstanceIndex,
4809 ty: TypeStreamTableIndex,
4810 options: OptionsIndex,
4811 stream: u32,
4812 address: u32,
4813 count: u32,
4814 ) -> Result<u32> {
4815 instance
4816 .guest_read(
4817 StoreContextMut(self),
4818 caller,
4819 TransmitIndex::Stream(ty),
4820 options,
4821 None,
4822 stream,
4823 address,
4824 count,
4825 )
4826 .map(|result| result.encode())
4827 }
4828
4829 fn future_drop_writable(
4830 &mut self,
4831 instance: Instance,
4832 ty: TypeFutureTableIndex,
4833 writer: u32,
4834 ) -> Result<()> {
4835 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4836 }
4837
4838 fn flat_stream_write(
4839 &mut self,
4840 instance: Instance,
4841 caller: RuntimeComponentInstanceIndex,
4842 ty: TypeStreamTableIndex,
4843 options: OptionsIndex,
4844 payload_size: u32,
4845 payload_align: u32,
4846 stream: u32,
4847 address: u32,
4848 count: u32,
4849 ) -> Result<u32> {
4850 instance
4851 .guest_write(
4852 StoreContextMut(self),
4853 caller,
4854 TransmitIndex::Stream(ty),
4855 options,
4856 Some(FlatAbi {
4857 size: payload_size,
4858 align: payload_align,
4859 }),
4860 stream,
4861 address,
4862 count,
4863 )
4864 .map(|result| result.encode())
4865 }
4866
4867 fn flat_stream_read(
4868 &mut self,
4869 instance: Instance,
4870 caller: RuntimeComponentInstanceIndex,
4871 ty: TypeStreamTableIndex,
4872 options: OptionsIndex,
4873 payload_size: u32,
4874 payload_align: u32,
4875 stream: u32,
4876 address: u32,
4877 count: u32,
4878 ) -> Result<u32> {
4879 instance
4880 .guest_read(
4881 StoreContextMut(self),
4882 caller,
4883 TransmitIndex::Stream(ty),
4884 options,
4885 Some(FlatAbi {
4886 size: payload_size,
4887 align: payload_align,
4888 }),
4889 stream,
4890 address,
4891 count,
4892 )
4893 .map(|result| result.encode())
4894 }
4895
4896 fn stream_drop_writable(
4897 &mut self,
4898 instance: Instance,
4899 ty: TypeStreamTableIndex,
4900 writer: u32,
4901 ) -> Result<()> {
4902 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4903 }
4904
4905 fn error_context_debug_message(
4906 &mut self,
4907 instance: Instance,
4908 ty: TypeComponentLocalErrorContextTableIndex,
4909 options: OptionsIndex,
4910 err_ctx_handle: u32,
4911 debug_msg_address: u32,
4912 ) -> Result<()> {
4913 instance.error_context_debug_message(
4914 StoreContextMut(self),
4915 ty,
4916 options,
4917 err_ctx_handle,
4918 debug_msg_address,
4919 )
4920 }
4921
4922 fn thread_new_indirect(
4923 &mut self,
4924 instance: Instance,
4925 caller: RuntimeComponentInstanceIndex,
4926 func_ty_idx: TypeFuncIndex,
4927 start_func_table_idx: RuntimeTableIndex,
4928 start_func_idx: u32,
4929 context: i32,
4930 ) -> Result<u32> {
4931 instance.thread_new_indirect(
4932 StoreContextMut(self),
4933 caller,
4934 func_ty_idx,
4935 start_func_table_idx,
4936 start_func_idx,
4937 context,
4938 )
4939 }
4940}
4941
4942type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4943
4944async fn run_with_host_task_set<F>(task: TableId<HostTask>, future: F) -> Result<F::Output>
4947where
4948 F: Future,
4949{
4950 let mut future = pin!(future);
4951 future::poll_fn(|cx| {
4952 let old_thread = match tls::get(|store| store.set_thread(task)) {
4953 Ok(thread) => thread,
4954 Err(error) => return Poll::Ready(Err(error)),
4955 };
4956 let result = future.as_mut().poll(cx);
4957 match tls::get(|store| store.set_thread(old_thread)) {
4958 Ok(_) => result.map(Ok),
4959 Err(error) => Poll::Ready(Err(error)),
4960 }
4961 })
4962 .await
4963}
4964
4965pub(crate) struct HostTask {
4969 common: WaitableCommon,
4970
4971 caller: TableId<GuestTask>,
4978
4979 call_context: CallContext,
4982
4983 state: HostTaskState,
4984}
4985
4986enum HostTaskState {
4987 CalleeStarted,
4992
4993 CalleeRunning(JoinHandle),
4998
4999 CalleeFinished(LiftedResult),
5003
5004 CalleeDone { cancelled: bool },
5007}
5008
5009impl HostTask {
5010 fn new(caller: TableId<GuestTask>, state: HostTaskState) -> Self {
5011 Self {
5012 common: WaitableCommon::default(),
5013 call_context: CallContext::default(),
5014 caller,
5015 state,
5016 }
5017 }
5018}
5019
5020impl TableDebug for HostTask {
5021 fn type_name() -> &'static str {
5022 "HostTask"
5023 }
5024}
5025
5026type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
5027
5028enum Caller {
5030 Host {
5032 tx: Option<oneshot::Sender<LiftedResult>>,
5034 host_future_present: bool,
5037 caller: Option<TableId<HostTask>>,
5041 },
5042 Guest {
5044 thread: QualifiedThreadId,
5046 },
5047}
5048
5049struct LiftResult {
5052 lift: RawLift,
5053 ty: TypeTupleIndex,
5054 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
5055 string_encoding: StringEncoding,
5056}
5057
5058#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5063pub(crate) struct QualifiedThreadId {
5064 task: TableId<GuestTask>,
5065 thread: TableId<GuestThread>,
5066}
5067
5068impl QualifiedThreadId {
5069 fn qualify(
5070 state: &mut ConcurrentState,
5071 thread: TableId<GuestThread>,
5072 ) -> Result<QualifiedThreadId> {
5073 Ok(QualifiedThreadId {
5074 task: state.get_mut(thread)?.parent_task,
5075 thread,
5076 })
5077 }
5078}
5079
5080impl fmt::Debug for QualifiedThreadId {
5081 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5082 f.debug_tuple("QualifiedThreadId")
5083 .field(&self.task.rep())
5084 .field(&self.thread.rep())
5085 .finish()
5086 }
5087}
5088
5089enum GuestThreadState {
5090 NotStartedImplicit,
5091 NotStartedExplicit(
5092 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
5093 ),
5094 Running,
5095 Suspended(StoreFiber<'static>),
5096 Ready {
5097 fiber: StoreFiber<'static>,
5098 },
5099 Completed,
5100}
5101
5102impl fmt::Debug for GuestThreadState {
5103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5104 match self {
5105 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
5106 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
5107 Self::Running => f.debug_tuple("Running").finish(),
5108 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5109 Self::Ready { .. } => f.debug_struct("Ready").finish(),
5110 Self::Completed => f.debug_tuple("Completed").finish(),
5111 }
5112 }
5113}
5114
5115#[derive(Copy, Clone, PartialEq, Eq, Debug)]
5116enum WakeOnCancel {
5117 None,
5118 Waiting(TableId<WaitableSet>),
5119 Yielding,
5120}
5121
5122impl WakeOnCancel {
5123 fn is_none(self) -> bool {
5124 matches!(self, WakeOnCancel::None)
5125 }
5126
5127 fn replace(&mut self, other: WakeOnCancel) -> Self {
5128 let old = *self;
5129 *self = other;
5130 old
5131 }
5132
5133 fn take(&mut self) -> Self {
5134 self.replace(WakeOnCancel::None)
5135 }
5136}
5137
5138pub struct GuestThread {
5139 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5142 parent_task: TableId<GuestTask>,
5144 wake_on_cancel: WakeOnCancel,
5147 state: GuestThreadState,
5149 instance_rep: Option<u32>,
5152 sync_call_set: TableId<WaitableSet>,
5154 old_do_not_suspend: Option<bool>,
5157}
5158
5159impl GuestThread {
5160 fn from_instance(
5163 state: Pin<&mut ComponentInstance>,
5164 caller_instance: RuntimeComponentInstanceIndex,
5165 guest_thread: u32,
5166 ) -> Result<TableId<Self>> {
5167 let rep = state.instance_states().0[caller_instance]
5168 .thread_handle_table()
5169 .guest_thread_rep(guest_thread)?;
5170 Ok(TableId::new(rep))
5171 }
5172
5173 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5174 let sync_call_set = state.push(WaitableSet {
5175 is_sync_call_set: true,
5176 ..WaitableSet::default()
5177 })?;
5178 Ok(Self {
5179 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5180 parent_task,
5181 wake_on_cancel: WakeOnCancel::None,
5182 state: GuestThreadState::NotStartedImplicit,
5183 instance_rep: None,
5184 sync_call_set,
5185 old_do_not_suspend: None,
5186 })
5187 }
5188
5189 fn new_explicit(
5190 state: &mut ConcurrentState,
5191 parent_task: TableId<GuestTask>,
5192 start_func: Box<
5193 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5194 >,
5195 ) -> Result<Self> {
5196 let sync_call_set = state.push(WaitableSet {
5197 is_sync_call_set: true,
5198 ..WaitableSet::default()
5199 })?;
5200 Ok(Self {
5201 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5202 parent_task,
5203 wake_on_cancel: WakeOnCancel::None,
5204 state: GuestThreadState::NotStartedExplicit(start_func),
5205 instance_rep: None,
5206 sync_call_set,
5207 old_do_not_suspend: None,
5208 })
5209 }
5210}
5211
5212impl TableDebug for GuestThread {
5213 fn type_name() -> &'static str {
5214 "GuestThread"
5215 }
5216}
5217
5218enum SyncResult {
5219 NotProduced,
5220 Produced(Option<ValRaw>),
5221 Taken,
5222}
5223
5224impl SyncResult {
5225 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5226 Ok(match mem::replace(self, SyncResult::Taken) {
5227 SyncResult::NotProduced => None,
5228 SyncResult::Produced(val) => Some(val),
5229 SyncResult::Taken => {
5230 bail_bug!("attempted to take a synchronous result that was already taken")
5231 }
5232 })
5233 }
5234}
5235
5236#[derive(Debug)]
5237enum HostFutureState {
5238 NotApplicable,
5239 Live,
5240 Dropped,
5241}
5242
5243pub(crate) struct GuestTask {
5245 common: WaitableCommon,
5247 lower_params: Option<RawLower>,
5249 lift_result: Option<LiftResult>,
5251 result: Option<LiftedResult>,
5254 callback: Option<CallbackFn>,
5257 caller: Caller,
5259 call_context: CallContext,
5264 sync_result: SyncResult,
5267 cancel_request_delivered: bool,
5271 starting_sent: bool,
5274 instance: RuntimeInstance,
5281 event: Option<Event>,
5284 exited: bool,
5286 threads: HashSet<TableId<GuestThread>>,
5288 host_future_state: HostFutureState,
5291 async_typed: bool,
5294 async_lifted: bool,
5297
5298 decremented_interesting_task_count: bool,
5299}
5300
5301impl GuestTask {
5302 fn already_lowered_parameters(&self) -> bool {
5303 self.lower_params.is_none()
5305 }
5306
5307 fn returned_or_cancelled(&self) -> bool {
5308 self.lift_result.is_none()
5310 }
5311
5312 fn ready_to_delete(&self) -> bool {
5313 let threads_completed = self.threads.is_empty();
5314 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5315 let pending_completion_event = matches!(
5316 self.common.event,
5317 Some(Event::Subtask {
5318 status: Status::Returned | Status::ReturnCancelled
5319 })
5320 );
5321 let ready = threads_completed
5322 && !has_sync_result
5323 && !pending_completion_event
5324 && !matches!(self.host_future_state, HostFutureState::Live);
5325 log::trace!(
5326 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5327 threads_completed,
5328 has_sync_result,
5329 pending_completion_event,
5330 self.host_future_state
5331 );
5332 ready
5333 }
5334
5335 fn new(
5336 state: &mut ConcurrentState,
5337 lower_params: RawLower,
5338 lift_result: LiftResult,
5339 caller: Caller,
5340 callback: Option<CallbackFn>,
5341 instance: RuntimeInstance,
5342 async_typed: bool,
5343 async_lifted: bool,
5344 ) -> Result<QualifiedThreadId> {
5345 let host_future_state = match &caller {
5346 Caller::Guest { .. } => HostFutureState::NotApplicable,
5347 Caller::Host {
5348 host_future_present,
5349 ..
5350 } => {
5351 if *host_future_present {
5352 HostFutureState::Live
5353 } else {
5354 HostFutureState::NotApplicable
5355 }
5356 }
5357 };
5358 let task = state.push(Self {
5359 common: WaitableCommon::default(),
5360 lower_params: Some(lower_params),
5361 lift_result: Some(lift_result),
5362 result: None,
5363 callback,
5364 caller,
5365 call_context: CallContext::default(),
5366 sync_result: SyncResult::NotProduced,
5367 cancel_request_delivered: false,
5368 starting_sent: false,
5369 instance,
5370 event: None,
5371 exited: false,
5372 threads: HashSet::new(),
5373 host_future_state,
5374 async_typed,
5375 async_lifted,
5376 decremented_interesting_task_count: false,
5377 })?;
5378 let new_thread = GuestThread::new_implicit(state, task)?;
5379 let thread = state.push(new_thread)?;
5380 state.get_mut(task)?.threads.insert(thread);
5381 state.interesting_tasks += 1;
5382 let thread = QualifiedThreadId { task, thread };
5383 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5384 Ok(thread)
5385 }
5386}
5387
5388impl TableDebug for GuestTask {
5389 fn type_name() -> &'static str {
5390 "GuestTask"
5391 }
5392}
5393
5394#[derive(Default)]
5396struct WaitableCommon {
5397 event: Option<Event>,
5399 set: Option<TableId<WaitableSet>>,
5401 handle: Option<u32>,
5403}
5404
5405#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5407enum Waitable {
5408 Host(TableId<HostTask>),
5410 Guest(TableId<GuestTask>),
5412 Transmit(TableId<TransmitHandle>),
5414}
5415
5416impl Waitable {
5417 fn from_instance(
5420 state: Pin<&mut ComponentInstance>,
5421 caller_instance: RuntimeComponentInstanceIndex,
5422 waitable: u32,
5423 ) -> Result<Self> {
5424 use crate::runtime::vm::component::Waitable;
5425
5426 let (waitable, kind) = state.instance_states().0[caller_instance]
5427 .handle_table()
5428 .waitable_rep(waitable)?;
5429
5430 Ok(match kind {
5431 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5432 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5433 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5434 })
5435 }
5436
5437 fn rep(&self) -> u32 {
5439 match self {
5440 Self::Host(id) => id.rep(),
5441 Self::Guest(id) => id.rep(),
5442 Self::Transmit(id) => id.rep(),
5443 }
5444 }
5445
5446 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5450 log::trace!("waitable {self:?} join set {set:?}");
5451
5452 let old = mem::replace(&mut self.common(state)?.set, set);
5453
5454 if let Some(old) = old {
5455 match *self {
5456 Waitable::Host(id) => state.remove_child(id, old),
5457 Waitable::Guest(id) => state.remove_child(id, old),
5458 Waitable::Transmit(id) => state.remove_child(id, old),
5459 }?;
5460
5461 state.get_mut(old)?.ready.remove(self);
5462 }
5463
5464 if let Some(set) = set {
5465 match *self {
5466 Waitable::Host(id) => state.add_child(id, set),
5467 Waitable::Guest(id) => state.add_child(id, set),
5468 Waitable::Transmit(id) => state.add_child(id, set),
5469 }?;
5470
5471 if self.common(state)?.event.is_some() {
5472 self.mark_ready(state)?;
5473 }
5474 }
5475
5476 Ok(())
5477 }
5478
5479 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5481 Ok(match self {
5482 Self::Host(id) => &mut state.get_mut(*id)?.common,
5483 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5484 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5485 })
5486 }
5487
5488 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5494 if self.common(state)?.set.is_some() {
5495 bail!(Trap::WaitableSyncAndAsync);
5496 }
5497 Ok(())
5498 }
5499
5500 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5504 log::trace!("set event for {self:?}: {event:?}");
5505 self.common(state)?.event = event;
5506 self.mark_ready(state)
5507 }
5508
5509 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5511 let common = self.common(state)?;
5512 let event = common.event.take();
5513 if let Some(set) = self.common(state)?.set {
5514 state.get_mut(set)?.ready.remove(self);
5515 }
5516
5517 Ok(event)
5518 }
5519
5520 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5524 if let Some(set) = self.common(state)?.set {
5525 let set_state = state.get_mut(set)?;
5526 set_state.ready.insert(*self);
5527
5528 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5529 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5530 assert!(wake_on_cancel.is_none() || wake_on_cancel == WakeOnCancel::Waiting(set));
5531
5532 let item = match mode {
5533 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5534 instance: state.get_mut(thread.task)?.instance,
5535 thread,
5536 fiber,
5537 }),
5538 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5539 instance: state.get_mut(thread.task)?.instance,
5540 call: GuestCall {
5541 thread,
5542 kind: GuestCallKind::DeliverEvent {
5543 instance,
5544 set: Some(set),
5545 },
5546 },
5547 }),
5548 };
5549
5550 if let Some(item) = item {
5551 state.push_high_priority(item);
5552 }
5553 }
5554 }
5555 Ok(())
5556 }
5557
5558 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5560 match self {
5561 Self::Host(task) => {
5562 log::trace!("delete host task {task:?}");
5563 state.delete(*task)?;
5564 }
5565 Self::Guest(task) => {
5566 log::trace!("delete guest task {task:?}");
5567 let task = state.delete(*task)?;
5568
5569 debug_assert!(task.decremented_interesting_task_count);
5576 }
5577 Self::Transmit(task) => {
5578 state.delete(*task)?;
5579 }
5580 }
5581
5582 Ok(())
5583 }
5584}
5585
5586impl fmt::Debug for Waitable {
5587 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5588 match self {
5589 Self::Host(id) => write!(f, "{id:?}"),
5590 Self::Guest(id) => write!(f, "{id:?}"),
5591 Self::Transmit(id) => write!(f, "{id:?}"),
5592 }
5593 }
5594}
5595
5596#[derive(Default)]
5598struct WaitableSet {
5599 ready: BTreeSet<Waitable>,
5601 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5603 is_sync_call_set: bool,
5606}
5607
5608impl TableDebug for WaitableSet {
5609 fn type_name() -> &'static str {
5610 "WaitableSet"
5611 }
5612}
5613
5614type RawLower =
5616 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5617
5618type RawLift = Box<
5620 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5621>;
5622
5623type LiftedResult = Box<dyn Any + Send + Sync>;
5627
5628struct DummyResult;
5631
5632#[derive(Default)]
5634pub struct ConcurrentInstanceState {
5635 backpressure: u16,
5637 do_not_enter: bool,
5639 do_not_suspend: bool,
5642 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5645}
5646
5647impl ConcurrentInstanceState {
5648 pub fn pending_is_empty(&self) -> bool {
5649 self.pending.is_empty()
5650 }
5651}
5652
5653#[derive(Debug, Copy, Clone)]
5654pub(crate) enum CurrentThread {
5655 Guest(QualifiedThreadId),
5658 Host(TableId<HostTask>),
5660 DeferredHost(QualifiedThreadId),
5663 GuestTask(TableId<GuestTask>),
5667 None,
5670}
5671
5672impl CurrentThread {
5673 fn guest(&self) -> Option<&QualifiedThreadId> {
5674 match self {
5675 Self::Guest(id) => Some(id),
5676 _ => None,
5677 }
5678 }
5679
5680 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5681 match self {
5682 Self::Guest(id) => Some(id.task),
5683 Self::GuestTask(id) => Some(*id),
5684 _ => None,
5685 }
5686 }
5687
5688 fn is_none(&self) -> bool {
5689 matches!(self, Self::None)
5690 }
5691}
5692
5693impl From<QualifiedThreadId> for CurrentThread {
5694 fn from(id: QualifiedThreadId) -> Self {
5695 Self::Guest(id)
5696 }
5697}
5698
5699impl From<TableId<HostTask>> for CurrentThread {
5700 fn from(id: TableId<HostTask>) -> Self {
5701 Self::Host(id)
5702 }
5703}
5704
5705enum Priority {
5706 Switch,
5707 High,
5708 Low,
5709}
5710
5711pub struct ConcurrentState {
5713 unforced_current_thread: CurrentThread,
5719
5720 deferred_host_call_context: Option<CallContext>,
5726
5727 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5732 table: AlwaysMut<ResourceTable>,
5734 switch_item: Option<WorkItem>,
5742 next_switch_item: Option<WorkItem>,
5748 high_priority: VecDeque<WorkItem>,
5750 low_priority: VecDeque<WorkItem>,
5752 suspend_reason: Option<SuspendReason>,
5756 worker: Option<StoreFiber<'static>>,
5760 worker_item: Option<WorkerItem>,
5762
5763 global_error_context_ref_counts:
5776 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5777
5778 interesting_tasks: usize,
5791
5792 interesting_tasks_empty_waker: Option<Waker>,
5796
5797 ready_for_concurrent_call_waker: Option<Waker>,
5802
5803 event_loop_running: bool,
5805}
5806
5807impl Default for ConcurrentState {
5808 fn default() -> Self {
5809 Self {
5810 unforced_current_thread: CurrentThread::None,
5811 deferred_host_call_context: None,
5812 table: AlwaysMut::new(ResourceTable::new()),
5813 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5814 switch_item: None,
5815 next_switch_item: None,
5816 high_priority: VecDeque::new(),
5817 low_priority: VecDeque::new(),
5818 suspend_reason: None,
5819 worker: None,
5820 worker_item: None,
5821 global_error_context_ref_counts: BTreeMap::new(),
5822 interesting_tasks: 0,
5823 interesting_tasks_empty_waker: None,
5824 ready_for_concurrent_call_waker: None,
5825 event_loop_running: false,
5826 }
5827 }
5828}
5829
5830impl ConcurrentState {
5831 pub(crate) fn take_fibers_and_futures(
5848 &mut self,
5849 fibers: &mut Vec<StoreFiber<'static>>,
5850 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5851 ) {
5852 let mut items = Vec::new();
5853 for entry in self.table.get_mut().iter_mut() {
5854 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5855 for mode in mem::take(&mut set.waiting).into_values() {
5856 match mode {
5857 WaitMode::Fiber(fiber) => {
5858 fibers.push(fiber);
5859 }
5860 WaitMode::Callback(_) => {}
5861 }
5862 }
5863 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5864 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5865 mem::replace(&mut thread.state, GuestThreadState::Completed)
5866 {
5867 fibers.push(fiber);
5868 }
5869 } else if let Some(item) = entry.downcast_mut::<Option<WorkItem>>() {
5870 if let Some(item) = item.take() {
5871 items.push(item);
5872 }
5873 }
5874 }
5875
5876 if let Some(fiber) = self.worker.take() {
5877 fibers.push(fiber);
5878 }
5879
5880 let mut handle_item = |item| match item {
5881 WorkItem::ResumeFiber { fiber, .. } => {
5882 fibers.push(fiber);
5883 }
5884 WorkItem::PushFuture(future) => {
5885 self.futures
5886 .get_mut()
5887 .as_mut()
5888 .unwrap()
5889 .push(future.into_inner());
5890 }
5891 WorkItem::ResumeThread { .. }
5892 | WorkItem::GuestCall { .. }
5893 | WorkItem::WorkerFunction(_) => {}
5894 };
5895
5896 for item in items {
5897 handle_item(item);
5898 }
5899 if let Some(item) = self.switch_item.take() {
5900 handle_item(item);
5901 }
5902 if let Some(item) = self.next_switch_item.take() {
5903 handle_item(item);
5904 }
5905 for item in mem::take(&mut self.high_priority) {
5906 handle_item(item);
5907 }
5908 for item in mem::take(&mut self.low_priority) {
5909 handle_item(item);
5910 }
5911
5912 if let Some(them) = self.futures.get_mut().take() {
5913 futures.push(them);
5914 }
5915 }
5916
5917 #[cfg(feature = "gc")]
5918 pub(crate) fn trace_fiber_roots(
5919 &mut self,
5920 modules: &ModuleRegistry,
5921 unwind: &dyn Unwind,
5922 gc_roots_list: &mut GcRootsList,
5923 ) {
5924 let ConcurrentState {
5925 table,
5926 worker,
5927 switch_item,
5928 next_switch_item,
5929 high_priority,
5930 low_priority,
5931
5932 futures: _,
5936
5937 worker_item: _,
5939 unforced_current_thread: _,
5940 deferred_host_call_context: _,
5941 suspend_reason: _,
5942 global_error_context_ref_counts: _,
5943 interesting_tasks: _,
5944 interesting_tasks_empty_waker: _,
5945 ready_for_concurrent_call_waker: _,
5946 event_loop_running: _,
5947 } = self;
5948
5949 for entry in table.get_mut().iter_mut() {
5950 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5951 for mode in set.waiting.values_mut() {
5952 match mode {
5953 WaitMode::Fiber(fiber) => {
5954 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5955 }
5956 WaitMode::Callback(_) => {}
5957 }
5958 }
5959 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5960 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5961 &mut thread.state
5962 {
5963 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5964 }
5965 } else if let Some(Some(WorkItem::ResumeFiber { fiber, .. })) =
5966 entry.downcast_mut::<Option<WorkItem>>()
5967 {
5968 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5969 }
5970 }
5971
5972 if let Some(fiber) = worker {
5973 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5974 }
5975
5976 let mut handle_item = |item: &mut WorkItem| match item {
5977 WorkItem::ResumeFiber { fiber, .. } => {
5978 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5979 }
5980 WorkItem::PushFuture(_future) => {
5981 }
5984 WorkItem::ResumeThread { .. }
5985 | WorkItem::GuestCall { .. }
5986 | WorkItem::WorkerFunction(_) => {}
5987 };
5988
5989 if let Some(item) = switch_item {
5990 handle_item(item);
5991 }
5992 if let Some(item) = next_switch_item {
5993 handle_item(item);
5994 }
5995 for item in high_priority {
5996 handle_item(item);
5997 }
5998 for item in low_priority {
5999 handle_item(item);
6000 }
6001 }
6002
6003 fn push<V: Send + Sync + 'static>(
6004 &mut self,
6005 value: V,
6006 ) -> Result<TableId<V>, ResourceTableError> {
6007 self.table.get_mut().push(value).map(TableId::from)
6008 }
6009
6010 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
6011 self.table.get_mut().get_mut(&Resource::from(id))
6012 }
6013
6014 pub fn add_child<T: 'static, U: 'static>(
6015 &mut self,
6016 child: TableId<T>,
6017 parent: TableId<U>,
6018 ) -> Result<(), ResourceTableError> {
6019 self.table
6020 .get_mut()
6021 .add_child(Resource::from(child), Resource::from(parent))
6022 }
6023
6024 pub fn remove_child<T: 'static, U: 'static>(
6025 &mut self,
6026 child: TableId<T>,
6027 parent: TableId<U>,
6028 ) -> Result<(), ResourceTableError> {
6029 self.table
6030 .get_mut()
6031 .remove_child(Resource::from(child), Resource::from(parent))
6032 }
6033
6034 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
6035 self.table.get_mut().delete(Resource::from(id))
6036 }
6037
6038 fn push_future(&mut self, future: HostTaskFuture) {
6039 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
6046 }
6047
6048 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
6049 log::trace!("set switch item: {item:?}");
6050
6051 if self.switch_item.is_some() {
6052 bail_bug!("switch item already set");
6053 }
6054
6055 self.switch_item = Some(item);
6056
6057 Ok(())
6058 }
6059
6060 fn take_next_switch_item(&mut self) -> Result<()> {
6061 if let Some(item) = self.next_switch_item.take() {
6062 self.set_switch_item(item)?;
6063 }
6064 Ok(())
6065 }
6066
6067 fn push_high_priority(&mut self, item: WorkItem) {
6068 log::trace!("push high priority: {item:?}");
6069 self.high_priority.push_front(item);
6070 }
6071
6072 fn push_low_priority(&mut self, item: WorkItem) {
6073 log::trace!("push low priority: {item:?}");
6074 self.low_priority.push_front(item);
6075 }
6076
6077 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
6078 match priority {
6079 Priority::Switch => self.set_switch_item(item)?,
6080 Priority::High => self.push_high_priority(item),
6081 Priority::Low => self.push_low_priority(item),
6082 }
6083
6084 Ok(())
6085 }
6086
6087 fn promote_instance_local_thread_work_item(
6088 &mut self,
6089 current_instance: RuntimeInstance,
6090 ) -> Result<bool> {
6091 log::trace!("promote thread work items for {current_instance:?}");
6092
6093 self.promote_work_item_matching(|item: &WorkItem| {
6094 let result = match item {
6095 WorkItem::ResumeThread { instance, .. }
6096 | WorkItem::ResumeFiber { instance, .. }
6097 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
6098 _ => false,
6099 };
6100
6101 log::trace!("candidate {item:?}: {result}");
6102 result
6103 })
6104 }
6105
6106 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
6107 self.promote_work_item_matching(|item: &WorkItem| match item {
6108 WorkItem::ResumeThread {
6109 thread: item_thread,
6110 ..
6111 }
6112 | WorkItem::GuestCall {
6113 call:
6114 GuestCall {
6115 thread: item_thread,
6116 ..
6117 },
6118 ..
6119 } => *item_thread == thread,
6120 _ => false,
6121 })
6122 }
6123
6124 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6125 where
6126 F: FnMut(&WorkItem) -> bool,
6127 {
6128 for item in mem::take(&mut self.high_priority).into_iter().rev() {
6133 if self.switch_item.is_none() && predicate(&item) {
6134 self.set_switch_item(item)?;
6135 } else {
6136 self.push_high_priority(item);
6137 }
6138 }
6139
6140 if self.switch_item.is_none() {
6141 for item in mem::take(&mut self.low_priority).into_iter().rev() {
6142 if self.switch_item.is_none() && predicate(&item) {
6143 self.set_switch_item(item)?;
6144 } else {
6145 self.push_low_priority(item);
6146 }
6147 }
6148 }
6149
6150 Ok(self.switch_item.is_some())
6151 }
6152
6153 pub fn call_context(&mut self, task: Scope) -> Result<&mut CallContext> {
6156 match task {
6157 Scope::HostId(task) => {
6158 let task: TableId<HostTask> = TableId::new(task);
6159 Ok(&mut self.get_mut(task)?.call_context)
6160 }
6161 Scope::Id(task) => {
6162 let task: TableId<GuestTask> = TableId::new(task);
6163 Ok(&mut self.get_mut(task)?.call_context)
6164 }
6165 }
6166 }
6167
6168 pub(crate) fn deferred_host_call_context(&mut self) -> Option<&mut CallContext> {
6169 self.deferred_host_call_context.as_mut()
6170 }
6171
6172 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6173 match self.futures.get_mut().as_mut() {
6174 Some(f) => Ok(f),
6175 None => bail_bug!("futures field of concurrent state is currently taken"),
6176 }
6177 }
6178
6179 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6180 self.table.get_mut()
6181 }
6182
6183 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
6185 let task = match cur {
6186 CurrentThread::GuestTask(task) => task,
6187 CurrentThread::Guest(thread) => thread.task,
6188 CurrentThread::Host(id) => {
6189 return Some(CurrentThread::GuestTask(self.get_mut(id).ok()?.caller));
6190 }
6191 CurrentThread::DeferredHost(caller) => return Some(caller.into()),
6192 CurrentThread::None => return None,
6193 };
6194 let task = self.get_mut(task).ok()?;
6195 Some(match task.caller {
6196 Caller::Host { caller, .. } => caller.map_or(CurrentThread::None, CurrentThread::Host),
6197 Caller::Guest { thread } => thread.into(),
6198 })
6199 }
6200
6201 fn debug_assert_deferred_host_invariant(&self) {
6202 debug_assert_eq!(
6203 self.deferred_host_call_context.is_some(),
6204 matches!(self.unforced_current_thread, CurrentThread::DeferredHost(_)),
6205 "a deferred host thread and call context must exist together",
6206 );
6207 }
6208
6209 fn materialize_host_task(&mut self) -> Result<CurrentThread> {
6210 self.debug_assert_deferred_host_invariant();
6211 let caller = match self.unforced_current_thread {
6212 CurrentThread::DeferredHost(caller) => caller,
6213 thread => return Ok(thread),
6214 };
6215
6216 let task = self.push(HostTask::new(caller.task, HostTaskState::CalleeStarted))?;
6218 let call_context = self
6219 .deferred_host_call_context
6220 .take()
6221 .expect("deferred host call context should be present");
6222 self.get_mut(task)
6223 .expect("newly inserted host task should be present")
6224 .call_context = call_context;
6225 self.unforced_current_thread = CurrentThread::Host(task);
6226 self.debug_assert_deferred_host_invariant();
6227 log::trace!("new host task materialized {task:?}");
6228 Ok(CurrentThread::Host(task))
6229 }
6230
6231 fn materialize_current_host_task_id(&mut self) -> Result<Option<TableId<HostTask>>> {
6232 match self.materialize_host_task()? {
6233 CurrentThread::Host(id) => Ok(Some(id)),
6234 CurrentThread::None => Ok(None),
6235 CurrentThread::Guest(_) | CurrentThread::GuestTask(_) => {
6236 bail_bug!("tried to materialize a host task id from a guest thread")
6237 }
6238 CurrentThread::DeferredHost(_) => {
6239 bail_bug!(
6240 "current thread is a deferred host thread which should have been materialized"
6241 )
6242 }
6243 }
6244 }
6245
6246 pub(crate) fn materialize_current_scope(&mut self) -> Result<Scope> {
6247 match self.materialize_host_task()? {
6248 CurrentThread::Host(id) => Ok(Scope::HostId(id.rep())),
6249 _ => bail_bug!("current scope is not a deferred host scope"),
6250 }
6251 }
6252}
6253
6254fn for_any_lower<
6257 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6258>(
6259 fun: F,
6260) -> F {
6261 fun
6262}
6263
6264fn for_any_lift<
6266 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6267>(
6268 fun: F,
6269) -> F {
6270 fun
6271}
6272
6273fn check_ambient_store(id: StoreId) {
6274 let message = "\
6275 `Future`s which depend on asynchronous component tasks, streams, or \
6276 futures to complete may only be polled from the event loop of the \
6277 store to which they belong. Please use \
6278 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6279 ";
6280 tls::try_get(|store| {
6281 let matched = match store {
6282 tls::TryGet::Some(store) => store.id() == id,
6283 tls::TryGet::Taken | tls::TryGet::None => false,
6284 };
6285
6286 if !matched {
6287 panic!("{message}")
6288 }
6289 });
6290}
6291
6292fn unpack_callback_code(code: u32) -> (u32, u32) {
6293 (code & 0xF, code >> 4)
6294}
6295
6296struct WaitableCheckParams {
6300 set: TableId<WaitableSet>,
6301 options: OptionsIndex,
6302 payload: u32,
6303}
6304
6305enum WaitableCheck {
6308 Wait,
6309 Poll,
6310}
6311
6312#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
6321pub struct GuestTaskId(TableId<GuestTask>);
6322
6323pub(crate) struct PreparedCall<R> {
6325 handle: Func,
6327 thread: QualifiedThreadId,
6329 param_count: usize,
6331 rx: oneshot::Receiver<LiftedResult>,
6334 runtime_instance: RuntimeInstance,
6336 _phantom: PhantomData<R>,
6337}
6338
6339impl<R> PreparedCall<R> {
6340 pub(crate) fn task_id(&self) -> TaskId {
6342 TaskId {
6343 task: self.thread.task,
6344 runtime_instance: self.runtime_instance,
6345 }
6346 }
6347}
6348
6349pub(crate) struct TaskId {
6351 task: TableId<GuestTask>,
6352 runtime_instance: RuntimeInstance,
6353}
6354
6355impl TaskId {
6356 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6362 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6363 let delete = if !task.already_lowered_parameters() {
6364 store.cancel_guest_subtask_without_lowered_parameters(
6365 self.runtime_instance,
6366 self.task,
6367 )?;
6368 true
6369 } else {
6370 task.host_future_state = HostFutureState::Dropped;
6371 task.ready_to_delete()
6372 };
6373 if delete {
6374 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
6375 }
6376 Ok(())
6377 }
6378}
6379
6380pub(crate) fn prepare_call<T, R>(
6386 mut store: StoreContextMut<T>,
6387 handle: Func,
6388 param_count: usize,
6389 host_future_present: bool,
6390 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6391 + Send
6392 + Sync
6393 + 'static,
6394 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6395 + Send
6396 + Sync
6397 + 'static,
6398) -> Result<PreparedCall<R>> {
6399 if !store.0.may_enter() {
6400 bail!(Trap::CannotEnterComponent);
6401 }
6402
6403 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6404
6405 let instance = handle.instance().id().get(store.0);
6406 let options = &instance.component().env_component().options[options];
6407 let ty = &instance.component().types()[ty];
6408 let async_typed = ty.async_;
6409 let async_lifted = raw_options.async_;
6410 let task_return_type = ty.results;
6411 let component_instance = raw_options.instance;
6412 let callback = options.callback.map(|i| instance.runtime_callback(i));
6413 let memory = options
6414 .memory()
6415 .map(|i| instance.runtime_memory(i))
6416 .map(SendSyncPtr::new);
6417 let string_encoding = options.string_encoding;
6418 let token = StoreToken::new(store.as_context_mut());
6419 let caller = store.0.materialize_host_task_id()?;
6420 let state = store.0.concurrent_state_mut()?;
6421
6422 let (tx, rx) = oneshot::channel();
6423
6424 let instance = handle.instance().runtime_instance(component_instance);
6425 let thread = GuestTask::new(
6426 state,
6427 Box::new(for_any_lower(move |store, params| {
6428 lower_params(token.as_context_mut(store), params)
6429 })),
6430 LiftResult {
6431 lift: Box::new(for_any_lift(move |store, result| {
6432 lift_result(store, result)
6433 })),
6434 ty: task_return_type,
6435 memory,
6436 string_encoding,
6437 },
6438 Caller::Host {
6439 tx: Some(tx),
6440 host_future_present,
6441 caller,
6442 },
6443 callback.map(|callback| {
6444 let callback = SendSyncPtr::new(callback);
6445 let instance = handle.instance();
6446 Box::new(move |store: &mut dyn VMStore, event, handle| {
6447 let store = token.as_context_mut(store);
6448 unsafe { instance.call_callback(store, callback, event, handle) }
6451 }) as CallbackFn
6452 }),
6453 instance,
6454 async_typed,
6455 async_lifted,
6456 )?;
6457
6458 Ok(PreparedCall {
6459 handle,
6460 thread,
6461 param_count,
6462 runtime_instance: instance,
6463 rx,
6464 _phantom: PhantomData,
6465 })
6466}
6467
6468pub(crate) struct StagedCall<R> {
6469 store: StoreId,
6470 task: TableId<GuestTask>,
6471 rx: oneshot::Receiver<LiftedResult>,
6472 _marker: PhantomData<fn() -> R>,
6473}
6474
6475impl<R> StagedCall<R> {
6476 pub(crate) fn new<T: 'static>(
6483 mut store: StoreContextMut<T>,
6484 prepared: PreparedCall<R>,
6485 ) -> Result<StagedCall<R>> {
6486 let PreparedCall {
6487 handle,
6488 thread,
6489 param_count,
6490 rx,
6491 ..
6492 } = prepared;
6493
6494 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6495
6496 Ok(StagedCall {
6497 store: store.0.id(),
6498 task: thread.task,
6499 rx,
6500 _marker: PhantomData,
6501 })
6502 }
6503
6504 fn task(&self) -> GuestTaskId {
6505 GuestTaskId(self.task)
6506 }
6507}
6508
6509impl<R> Future for StagedCall<R>
6510where
6511 R: 'static,
6512{
6513 type Output = Result<R>;
6514
6515 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6516 check_ambient_store(self.store);
6517 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6518 Ok(r) => match r.downcast() {
6519 Ok(r) => Ok(*r),
6520 Err(_) => bail_bug!("wrong type of value produced"),
6521 },
6522 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6523 })
6524 }
6525}
6526
6527fn stage_call0<T: 'static>(
6530 store: StoreContextMut<T>,
6531 handle: Func,
6532 guest_thread: QualifiedThreadId,
6533 param_count: usize,
6534) -> Result<()> {
6535 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6536 let is_concurrent = raw_options.async_;
6537 let callback = raw_options.callback;
6538 let instance = handle.instance();
6539 let callee = handle.lifted_core_func(store.0);
6540 let post_return = raw_options
6541 .post_return
6542 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6543 let callback = callback.map(|i| {
6544 let instance = instance.id().get(store.0);
6545 SendSyncPtr::new(instance.runtime_callback(i))
6546 });
6547
6548 log::trace!("queueing call {guest_thread:?}");
6549
6550 unsafe {
6554 instance.stage_call(
6555 store,
6556 guest_thread,
6557 SendSyncPtr::new(callee),
6558 param_count,
6559 1,
6560 is_concurrent,
6561 callback,
6562 post_return.map(SendSyncPtr::new),
6563 true,
6564 )
6565 }
6566}