1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::bail_bug;
55use crate::component::func::{Func, call_post_return};
56use crate::component::{
57 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
58};
59use crate::fiber::{self, StoreFiber, StoreFiberYield};
60use crate::hash_set::HashSet;
61#[cfg(feature = "gc")]
62use crate::module::ModuleRegistry;
63use crate::prelude::*;
64use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
65#[cfg(feature = "gc")]
66use crate::vm::GcRootsList;
67use crate::vm::component::{CallContext, ComponentInstance, InstanceState};
68use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore};
69use crate::{
70 AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, bail,
71};
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 AccessorTask<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 AccessorTask<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<T, D = HasSelf<T>>: Send + 'static
629where
630 D: HasData + ?Sized,
631{
632 fn run(self, accessor: &Accessor<T, D>) -> impl Future<Output = Result<()>> + Send;
634}
635
636enum CallerInfo {
639 Async {
641 params: Vec<ValRaw>,
642 has_result: bool,
643 },
644 Sync {
646 params: Vec<ValRaw>,
647 result_count: u32,
648 },
649}
650
651enum WaitMode {
653 Fiber(StoreFiber<'static>),
655 Callback(Instance),
658}
659
660#[derive(Debug)]
662enum SuspendReason {
663 Waiting {
666 set: TableId<WaitableSet>,
667 thread: QualifiedThreadId,
668 skip_may_block_check: bool,
669 },
670 NeedWork,
673 Yielding {
676 thread: QualifiedThreadId,
677 cancellable: bool,
678 skip_may_block_check: bool,
679 },
680 ExplicitlySuspending {
682 thread: QualifiedThreadId,
683 skip_may_block_check: bool,
684 },
685}
686
687enum GuestCallKind {
689 DeliverEvent {
692 instance: Instance,
694 set: Option<TableId<WaitableSet>>,
699 },
700 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>),
706 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
707}
708
709impl fmt::Debug for GuestCallKind {
710 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
711 match self {
712 Self::DeliverEvent { instance, set } => f
713 .debug_struct("DeliverEvent")
714 .field("instance", instance)
715 .field("set", set)
716 .finish(),
717 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
718 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
719 }
720 }
721}
722
723#[derive(Copy, Clone, Debug)]
725pub enum SuspensionTarget {
726 SomeSuspended(u32),
727 Some(u32),
728 None,
729}
730
731impl SuspensionTarget {
732 fn is_none(&self) -> bool {
733 matches!(self, SuspensionTarget::None)
734 }
735 fn is_some(&self) -> bool {
736 !self.is_none()
737 }
738}
739
740#[derive(Debug)]
742struct GuestCall {
743 thread: QualifiedThreadId,
744 kind: GuestCallKind,
745}
746
747impl GuestCall {
748 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
758 let instance = store
759 .concurrent_state_mut()?
760 .get_mut(self.thread.task)?
761 .instance;
762 let state = store.instance_state(instance).concurrent_state();
763
764 let ready = match &self.kind {
765 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
766 GuestCallKind::StartImplicit(_) => !(state.do_not_enter || state.backpressure > 0),
767 GuestCallKind::StartExplicit(_) => true,
768 };
769 log::trace!(
770 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
771 state.do_not_enter,
772 state.backpressure
773 );
774 Ok(ready)
775 }
776}
777
778enum WorkerItem {
780 GuestCall(GuestCall),
781 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
782}
783
784enum WorkItem {
787 PushFuture(AlwaysMut<HostTaskFuture>),
789 ResumeFiber(StoreFiber<'static>),
791 ResumeThread(RuntimeComponentInstanceIndex, QualifiedThreadId),
793 GuestCall(RuntimeComponentInstanceIndex, GuestCall),
795 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
797}
798
799impl fmt::Debug for WorkItem {
800 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
801 match self {
802 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
803 Self::ResumeFiber(_) => f.debug_tuple("ResumeFiber").finish(),
804 Self::ResumeThread(instance, thread) => f
805 .debug_tuple("ResumeThread")
806 .field(instance)
807 .field(thread)
808 .finish(),
809 Self::GuestCall(instance, call) => f
810 .debug_tuple("GuestCall")
811 .field(instance)
812 .field(call)
813 .finish(),
814 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
815 }
816 }
817}
818
819#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
821pub(crate) enum WaitResult {
822 Cancelled,
823 Completed,
824}
825
826pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
834 store: &mut dyn VMStore,
835 future: impl Future<Output = Result<R>> + Send + 'static,
836) -> Result<R> {
837 let task = store.current_host_thread()?;
838
839 let mut future = Box::pin(async move {
843 let result = future.await?;
844 tls::get(move |store| {
845 let state = store.concurrent_state_mut()?;
846 let host_state = &mut state.get_mut(task)?.state;
847 assert!(matches!(host_state, HostTaskState::CalleeStarted));
848 *host_state = HostTaskState::CalleeFinished(Box::new(result));
849
850 Waitable::Host(task).set_event(
851 state,
852 Some(Event::Subtask {
853 status: Status::Returned,
854 }),
855 )?;
856
857 Ok(())
858 })
859 }) as HostTaskFuture;
860
861 let poll = tls::set(store, || {
865 future
866 .as_mut()
867 .poll(&mut Context::from_waker(&Waker::noop()))
868 });
869
870 match poll {
871 Poll::Ready(result) => result?,
873
874 Poll::Pending => {
879 let state = store.concurrent_state_mut()?;
880 state.push_future(future);
881
882 let caller = state.get_mut(task)?.caller;
883 let set = state.get_mut(caller.thread)?.sync_call_set;
884 Waitable::Host(task).join(state, Some(set))?;
885
886 store.suspend(SuspendReason::Waiting {
887 set,
888 thread: caller,
889 skip_may_block_check: false,
890 })?;
891
892 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
896 }
897 }
898
899 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
901 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
902 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
903 Ok(result) => *result,
904 Err(_) => bail_bug!("host task finished with wrong type of result"),
905 }),
906 _ => bail_bug!("unexpected host task state after completion"),
907 }
908}
909
910fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
912 let mut next = Some(call);
913 while let Some(call) = next.take() {
914 match call.kind {
915 GuestCallKind::DeliverEvent { instance, set } => {
916 let (event, waitable) =
917 match instance.get_event(store, call.thread.task, set, true)? {
918 Some(pair) => pair,
919 None => bail_bug!("delivering non-present event"),
920 };
921 let state = store.concurrent_state_mut()?;
922 let task = state.get_mut(call.thread.task)?;
923 let runtime_instance = task.instance;
924 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
925
926 log::trace!(
927 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
928 call.thread,
929 );
930
931 let old_thread = store.set_thread(call.thread)?;
932 log::trace!(
933 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
934 call.thread
935 );
936
937 store.enter_instance(runtime_instance);
938
939 let Some(callback) = store
940 .concurrent_state_mut()?
941 .get_mut(call.thread.task)?
942 .callback
943 .take()
944 else {
945 bail_bug!("guest task callback field not present")
946 };
947
948 let code = callback(store, event, handle)?;
949
950 store
951 .concurrent_state_mut()?
952 .get_mut(call.thread.task)?
953 .callback = Some(callback);
954
955 store.exit_instance(runtime_instance)?;
956
957 store.set_thread(old_thread)?;
958
959 next = instance.handle_callback_code(
960 store,
961 call.thread,
962 runtime_instance.index,
963 code,
964 )?;
965
966 log::trace!(
967 "GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread"
968 );
969 }
970 GuestCallKind::StartImplicit(fun) => {
971 next = fun(store)?;
972 }
973 GuestCallKind::StartExplicit(fun) => {
974 fun(store)?;
975 }
976 }
977 }
978
979 Ok(())
980}
981
982impl<T> Store<T> {
983 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
985 where
986 T: Send + 'static,
987 {
988 ensure!(
989 self.as_context().0.concurrency_support(),
990 "cannot use `run_concurrent` when Config::concurrency_support disabled",
991 );
992 self.as_context_mut().run_concurrent(fun).await
993 }
994
995 #[doc(hidden)]
996 pub fn assert_concurrent_state_empty(&mut self) {
997 self.as_context_mut().assert_concurrent_state_empty();
998 }
999
1000 #[doc(hidden)]
1001 pub fn concurrent_state_table_size(&mut self) -> usize {
1002 self.as_context_mut().concurrent_state_table_size()
1003 }
1004
1005 pub fn spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> Result<JoinHandle>
1007 where
1008 T: 'static,
1009 {
1010 self.as_context_mut().spawn(task)
1011 }
1012}
1013
1014impl<T> StoreContextMut<'_, T> {
1015 #[doc(hidden)]
1026 pub fn assert_concurrent_state_empty(self) {
1027 let store = self.0;
1028 store
1029 .store_data_mut()
1030 .components
1031 .assert_instance_states_empty();
1032 let state = store.concurrent_state_mut().unwrap();
1033 assert!(
1034 state.table.get_mut().is_empty(),
1035 "non-empty table: {:?}",
1036 state.table.get_mut()
1037 );
1038 assert!(state.high_priority.is_empty());
1039 assert!(state.low_priority.is_empty());
1040 assert!(state.unforced_current_thread.is_none());
1041 assert!(state.futures_mut().unwrap().is_empty());
1042 assert!(state.global_error_context_ref_counts.is_empty());
1043 }
1044
1045 #[doc(hidden)]
1050 pub fn concurrent_state_table_size(&mut self) -> usize {
1051 self.0
1052 .concurrent_state_mut()
1053 .unwrap()
1054 .table
1055 .get_mut()
1056 .iter_mut()
1057 .count()
1058 }
1059
1060 pub fn spawn(mut self, task: impl AccessorTask<T>) -> Result<JoinHandle>
1070 where
1071 T: 'static,
1072 {
1073 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1074 self.spawn_with_accessor(accessor, task)
1075 }
1076
1077 fn spawn_with_accessor<D>(
1080 self,
1081 accessor: Accessor<T, D>,
1082 task: impl AccessorTask<T, D>,
1083 ) -> Result<JoinHandle>
1084 where
1085 T: 'static,
1086 D: HasData + ?Sized,
1087 {
1088 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1092 self.0
1093 .concurrent_state_mut()?
1094 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1095 Ok(handle)
1096 }
1097
1098 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1182 where
1183 T: Send + 'static,
1184 {
1185 ensure!(
1186 self.0.concurrency_support(),
1187 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1188 );
1189 self.do_run_concurrent(fun, false).await
1190 }
1191
1192 pub(super) async fn run_concurrent_trap_on_idle<R>(
1193 self,
1194 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1195 ) -> Result<R>
1196 where
1197 T: Send + 'static,
1198 {
1199 self.do_run_concurrent(fun, true).await
1200 }
1201
1202 async fn do_run_concurrent<R>(
1203 mut self,
1204 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1205 trap_on_idle: bool,
1206 ) -> Result<R>
1207 where
1208 T: Send + 'static,
1209 {
1210 debug_assert!(self.0.concurrency_support());
1211 check_recursive_run();
1212 let token = StoreToken::new(self.as_context_mut());
1213
1214 struct Dropper<'a, T: 'static, V> {
1215 store: StoreContextMut<'a, T>,
1216 value: ManuallyDrop<V>,
1217 }
1218
1219 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1220 fn drop(&mut self) {
1221 tls::set(self.store.0, || {
1222 unsafe { ManuallyDrop::drop(&mut self.value) }
1227 });
1228 }
1229 }
1230
1231 let accessor = &Accessor::new(token);
1232 let dropper = &mut Dropper {
1233 store: self,
1234 value: ManuallyDrop::new(fun(accessor)),
1235 };
1236 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1238
1239 dropper
1240 .store
1241 .as_context_mut()
1242 .poll_until(future, trap_on_idle)
1243 .await
1244 }
1245
1246 async fn poll_until<R>(
1252 mut self,
1253 mut future: Pin<&mut impl Future<Output = R>>,
1254 trap_on_idle: bool,
1255 ) -> Result<R>
1256 where
1257 T: Send + 'static,
1258 {
1259 struct Reset<'a, T: 'static> {
1260 store: StoreContextMut<'a, T>,
1261 futures: Option<FuturesUnordered<HostTaskFuture>>,
1262 }
1263
1264 impl<'a, T> Drop for Reset<'a, T> {
1265 fn drop(&mut self) {
1266 if let Some(futures) = self.futures.take() {
1267 *self
1268 .store
1269 .0
1270 .concurrent_state_mut_already_forced_current_thread()
1271 .futures
1272 .get_mut() = Some(futures);
1273 }
1274 }
1275 }
1276
1277 loop {
1278 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1282 let mut reset = Reset {
1283 store: self.as_context_mut(),
1284 futures,
1285 };
1286 let mut next = match reset.futures.as_mut() {
1287 Some(f) => pin!(f.next()),
1288 None => bail_bug!("concurrent state missing futures field"),
1289 };
1290
1291 enum PollResult<R> {
1292 Complete(R),
1293 ProcessWork {
1294 ready: Vec<WorkItem>,
1295 low_priority: bool,
1296 },
1297 }
1298
1299 let result = future::poll_fn(|cx| {
1300 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1303 return Poll::Ready(Ok(PollResult::Complete(value)));
1304 }
1305
1306 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1310 Poll::Ready(Some(output)) => {
1311 match output {
1312 Err(e) => return Poll::Ready(Err(e)),
1313 Ok(()) => {}
1314 }
1315 Poll::Ready(true)
1316 }
1317 Poll::Ready(None) => Poll::Ready(false),
1318 Poll::Pending => Poll::Pending,
1319 };
1320
1321 let state = reset.store.0.concurrent_state_mut()?;
1325 let mut ready = mem::take(&mut state.high_priority);
1326 let mut low_priority = false;
1327 if ready.is_empty() {
1328 if let Some(item) = state.low_priority.pop_back() {
1329 ready.push(item);
1330 low_priority = true;
1331 }
1332 }
1333 if !ready.is_empty() {
1334 return Poll::Ready(Ok(PollResult::ProcessWork {
1335 ready,
1336 low_priority,
1337 }));
1338 }
1339
1340 return match next {
1344 Poll::Ready(true) => {
1345 Poll::Ready(Ok(PollResult::ProcessWork {
1351 ready: Vec::new(),
1352 low_priority: false,
1353 }))
1354 }
1355 Poll::Ready(false) => {
1356 if let Poll::Ready(value) =
1360 tls::set(reset.store.0, || future.as_mut().poll(cx))
1361 {
1362 Poll::Ready(Ok(PollResult::Complete(value)))
1363 } else {
1364 if trap_on_idle {
1370 Poll::Ready(Err(Trap::AsyncDeadlock.into()))
1373 } else {
1374 Poll::Pending
1378 }
1379 }
1380 }
1381 Poll::Pending => Poll::Pending,
1386 };
1387 })
1388 .await;
1389
1390 drop(reset);
1394
1395 match result? {
1396 PollResult::Complete(value) => break Ok(value),
1399 PollResult::ProcessWork {
1402 ready,
1403 low_priority,
1404 } => {
1405 struct Dispose<'a, T: 'static, I: Iterator<Item = WorkItem>> {
1406 store: StoreContextMut<'a, T>,
1407 ready: I,
1408 }
1409
1410 impl<'a, T, I: Iterator<Item = WorkItem>> Drop for Dispose<'a, T, I> {
1411 fn drop(&mut self) {
1412 while let Some(item) = self.ready.next() {
1413 match item {
1414 WorkItem::ResumeFiber(mut fiber) => fiber.dispose(self.store.0),
1415 WorkItem::PushFuture(future) => {
1416 tls::set(self.store.0, move || drop(future))
1417 }
1418 _ => {}
1419 }
1420 }
1421 }
1422 }
1423
1424 let mut dispose = Dispose {
1425 store: self.as_context_mut(),
1426 ready: ready.into_iter(),
1427 };
1428
1429 if low_priority {
1451 dispose.store.0.yield_now().await
1452 }
1453
1454 while let Some(item) = dispose.ready.next() {
1455 dispose
1456 .store
1457 .as_context_mut()
1458 .handle_work_item(item)
1459 .await?;
1460 }
1461 }
1462 }
1463 }
1464 }
1465
1466 async fn handle_work_item(self, item: WorkItem) -> Result<()>
1468 where
1469 T: Send,
1470 {
1471 log::trace!("handle work item {item:?}");
1472 match item {
1473 WorkItem::PushFuture(future) => {
1474 self.0
1475 .concurrent_state_mut()?
1476 .futures_mut()?
1477 .push(future.into_inner());
1478 }
1479 WorkItem::ResumeFiber(fiber) => {
1480 self.0.resume_fiber(fiber).await?;
1481 }
1482 WorkItem::ResumeThread(_, thread) => {
1483 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1484 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1485 GuestThreadState::Running,
1486 ) {
1487 self.0.resume_fiber(fiber).await?;
1488 } else {
1489 bail_bug!("cannot resume non-pending thread {thread:?}");
1490 }
1491 }
1492 WorkItem::GuestCall(_, call) => {
1493 if call.is_ready(self.0)? {
1494 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1495 } else {
1496 let state = self.0.concurrent_state_mut()?;
1497 let task = state.get_mut(call.thread.task)?;
1498 if !task.starting_sent {
1499 task.starting_sent = true;
1500 if let GuestCallKind::StartImplicit(_) = &call.kind {
1501 Waitable::Guest(call.thread.task).set_event(
1502 state,
1503 Some(Event::Subtask {
1504 status: Status::Starting,
1505 }),
1506 )?;
1507 }
1508 }
1509
1510 let instance = state.get_mut(call.thread.task)?.instance;
1511 self.0
1512 .instance_state(instance)
1513 .concurrent_state()
1514 .pending
1515 .insert(call.thread, call.kind);
1516 }
1517 }
1518 WorkItem::WorkerFunction(fun) => {
1519 self.run_on_worker(WorkerItem::Function(fun)).await?;
1520 }
1521 }
1522
1523 Ok(())
1524 }
1525
1526 async fn run_on_worker(self, item: WorkerItem) -> Result<()>
1528 where
1529 T: Send,
1530 {
1531 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1532 fiber
1533 } else {
1534 fiber::make_fiber(self.0, move |store| {
1535 loop {
1536 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1537 bail_bug!("worker_item not present when resuming fiber")
1538 };
1539 match item {
1540 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1541 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1542 }
1543
1544 store.suspend(SuspendReason::NeedWork)?;
1545 }
1546 })?
1547 };
1548
1549 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1550 assert!(worker_item.is_none());
1551 *worker_item = Some(item);
1552
1553 self.0.resume_fiber(worker).await
1554 }
1555
1556 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1561 where
1562 T: 'static,
1563 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1564 + Send
1565 + Sync
1566 + 'static,
1567 R: Send + Sync + 'static,
1568 {
1569 let token = StoreToken::new(self);
1570 async move {
1571 let mut accessor = Accessor::new(token);
1572 closure(&mut accessor).await
1573 }
1574 }
1575
1576 pub fn async_call_stack(&mut self) -> Result<impl Iterator<Item = GuestTaskId>> {
1586 let mut cur = Some(self.0.current_thread()?);
1587 let state = self.0.concurrent_state_mut()?;
1588 Ok(core::iter::from_fn(move || {
1589 while let Some(t) = cur {
1590 cur = state.parent(t);
1591 if let Some(thread) = t.guest() {
1592 return Some(GuestTaskId(thread.task));
1593 }
1594 }
1595
1596 None
1597 }))
1598 }
1599}
1600
1601impl StoreOpaque {
1602 #[inline]
1605 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1606 if !self.concurrency_support() {
1608 return Ok(CurrentThread::None);
1609 }
1610
1611 if !self
1614 .vm_store_context_mut()
1615 .current_thread_mut()
1616 .is_deferred()
1617 {
1618 return Ok(self
1619 .concurrent_state_mut_already_forced_current_thread()
1620 .unforced_current_thread);
1621 }
1622
1623 self.force_deferred_current_thread()
1624 }
1625
1626 #[cold]
1629 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1630 let state = self.concurrent_state_mut_without_forcing_current_thread();
1639 let id = match state.unforced_current_thread.guest().copied() {
1640 Some(thread) => state.get_mut(thread.task)?.instance.instance,
1641 None => bail_bug!("deferred component-model thread with non-guest base"),
1642 };
1643
1644 let mut frames = Vec::new();
1647 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1648 while let Some(ptr) = cur.as_deferred() {
1649 let deferred = unsafe { ptr.as_non_null().as_ref() };
1654 frames.push((
1655 deferred.callee_async != 0,
1656 deferred.callee_instance,
1657 deferred.saved_context,
1658 ));
1659 cur = deferred.parent;
1660 }
1661
1662 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1666
1667 let current_context = *self.vm_store_context_mut().component_context_mut();
1670
1671 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1675 *self.vm_store_context_mut().component_context_mut() = saved_context;
1679 let callee = RuntimeInstance {
1680 instance: id,
1681 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1682 };
1683 self.enter_guest_sync_call(None, callee_async, callee)?;
1684 }
1685
1686 *self.vm_store_context_mut().component_context_mut() = current_context;
1688
1689 Ok(self
1690 .concurrent_state_mut_without_forcing_current_thread()
1691 .unforced_current_thread)
1692 }
1693
1694 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1695 match self.current_thread()?.guest() {
1696 Some(id) => Ok(*id),
1697 None => bail_bug!("current thread is not a guest thread"),
1698 }
1699 }
1700
1701 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1702 match self.current_thread()?.host() {
1703 Some(id) => Ok(id),
1704 None => bail_bug!("current thread is not a host thread"),
1705 }
1706 }
1707
1708 fn take_pending_cancellation(&mut self) -> Result<bool> {
1711 let thread = self.current_guest_thread()?;
1712 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1713 if let Some(Event::Cancelled) = task.event {
1714 task.event.take();
1715 return Ok(true);
1716 }
1717 Ok(false)
1718 }
1719
1720 pub(crate) fn enter_guest_sync_call(
1732 &mut self,
1733 guest_caller: Option<RuntimeInstance>,
1734 callee_async: bool,
1735 callee: RuntimeInstance,
1736 ) -> Result<()> {
1737 log::trace!("enter sync call {callee:?}");
1738 if !self.concurrency_support() {
1739 return self.enter_call_not_concurrent();
1740 }
1741
1742 let thread = self.current_thread()?;
1743 let state = self.concurrent_state_mut()?;
1744 let instance = if let Some(thread) = thread.guest() {
1745 Some(state.get_mut(thread.task)?.instance)
1746 } else {
1747 None
1748 };
1749 if guest_caller.is_some() {
1750 debug_assert_eq!(instance, guest_caller);
1751 }
1752 let guest_thread = GuestTask::new(
1753 state,
1754 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1755 LiftResult {
1756 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1757 ty: TypeTupleIndex::reserved_value(),
1758 memory: None,
1759 string_encoding: StringEncoding::Utf8,
1760 },
1761 if let Some(thread) = thread.guest() {
1762 Caller::Guest { thread: *thread }
1763 } else {
1764 Caller::Host {
1765 tx: None,
1766 host_future_present: false,
1767 caller: thread,
1768 }
1769 },
1770 None,
1771 callee,
1772 callee_async,
1773 )?;
1774
1775 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1776 guest_thread.thread,
1777 self,
1778 callee.index,
1779 )?;
1780 self.set_thread(guest_thread)?;
1781
1782 Ok(())
1783 }
1784
1785 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1793 if !self.concurrency_support() {
1794 return Ok(self.exit_call_not_concurrent());
1795 }
1796 let thread = match self.set_thread(CurrentThread::None)?.guest() {
1797 Some(t) => *t,
1798 None => bail_bug!("expected task when exiting"),
1799 };
1800 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1801 let instance = task.instance;
1802 let caller = match &task.caller {
1803 &Caller::Guest { thread } => thread.into(),
1804 &Caller::Host { caller, .. } => caller,
1805 };
1806 task.lift_result = None;
1807 task.exited = true;
1808 self.set_thread(caller)?;
1809
1810 log::trace!("exit sync call {instance:?}");
1811 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1812
1813 Ok(())
1814 }
1815
1816 pub(crate) fn host_task_create(&mut self) -> Result<Option<TableId<HostTask>>> {
1824 if !self.concurrency_support() {
1825 self.enter_call_not_concurrent()?;
1826 return Ok(None);
1827 }
1828 let caller = self.current_guest_thread()?;
1829 let state = self.concurrent_state_mut()?;
1830 let task = state.push(HostTask::new(caller, HostTaskState::CalleeStarted))?;
1831 log::trace!("new host task {task:?}");
1832 self.set_thread(task)?;
1833 Ok(Some(task))
1834 }
1835
1836 pub fn host_task_reenter_caller(&mut self) -> Result<()> {
1842 if !self.concurrency_support() {
1843 return Ok(());
1844 }
1845 let task = self.current_host_thread()?;
1846 let caller = self.concurrent_state_mut()?.get_mut(task)?.caller;
1847 self.set_thread(caller)?;
1848 Ok(())
1849 }
1850
1851 pub(crate) fn host_task_delete(&mut self, task: Option<TableId<HostTask>>) -> Result<()> {
1858 match task {
1859 Some(task) => {
1860 log::trace!("delete host task {task:?}");
1861 self.concurrent_state_mut()?.delete(task)?;
1862 }
1863 None => {
1864 self.exit_call_not_concurrent();
1865 }
1866 }
1867 Ok(())
1868 }
1869
1870 pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> Result<bool> {
1878 if self.trapped() {
1879 return Ok(false);
1880 }
1881 if !self.concurrency_support() {
1882 return Ok(true);
1883 }
1884 let mut cur = Some(self.current_thread()?);
1885 let state = self.concurrent_state_mut()?;
1886 while let Some(t) = cur {
1887 if let Some(thread) = t.guest() {
1888 let task = state.get_mut(thread.task)?;
1889 if task.instance.instance == instance.instance {
1896 return Ok(false);
1897 }
1898 }
1899 cur = state.parent(t);
1900 }
1901 Ok(true)
1902 }
1903
1904 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
1907 self.component_instance_mut(instance.instance)
1908 .instance_state(instance.index)
1909 }
1910
1911 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
1917 let thread = thread.into();
1918 let state = self.concurrent_state_mut()?;
1919 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
1920
1921 if let Some(old_thread) = old_thread.guest() {
1929 let old_context = *self.vm_store_context_mut().component_context_mut();
1930 self.concurrent_state_mut()?
1931 .get_mut(old_thread.thread)?
1932 .context = old_context;
1933 }
1934 if cfg!(debug_assertions) {
1935 *self.vm_store_context_mut().component_context_mut() =
1936 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
1937 }
1938 if let Some(thread) = thread.guest() {
1939 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1940 let context = thread.context;
1941 if cfg!(debug_assertions) {
1942 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
1943 }
1944 *self.vm_store_context_mut().component_context_mut() = context;
1945 }
1946
1947 let state = self.concurrent_state_mut()?;
1955 if let Some(old_thread) = old_thread.guest() {
1956 let instance = state.get_mut(old_thread.task)?.instance.instance;
1957 self.component_instance_mut(instance)
1958 .set_task_may_block(false)
1959 }
1960
1961 if thread.guest().is_some() {
1962 self.set_task_may_block()?;
1963 }
1964
1965 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
1967 VMLazyThread::none()
1968 } else {
1969 VMLazyThread::forced()
1970 };
1971
1972 Ok(old_thread)
1973 }
1974
1975 fn set_task_may_block(&mut self) -> Result<()> {
1978 let guest_thread = self.current_guest_thread()?;
1979 let state = self.concurrent_state_mut()?;
1980 let instance = state.get_mut(guest_thread.task)?.instance.instance;
1981 let may_block = self.concurrent_state_mut()?.may_block(guest_thread.task)?;
1982 self.component_instance_mut(instance)
1983 .set_task_may_block(may_block);
1984 Ok(())
1985 }
1986
1987 pub(crate) fn check_blocking(&mut self) -> Result<()> {
1988 if !self.concurrency_support() {
1989 return Ok(());
1990 }
1991 let task = self.current_guest_thread()?.task;
1992 let state = self.concurrent_state_mut()?;
1993 let instance = state.get_mut(task)?.instance.instance;
1994 let task_may_block = self.component_instance(instance).get_task_may_block();
1995
1996 if task_may_block {
1997 Ok(())
1998 } else {
1999 Err(Trap::CannotBlockSyncTask.into())
2000 }
2001 }
2002
2003 fn enter_instance(&mut self, instance: RuntimeInstance) {
2007 log::trace!("enter {instance:?}");
2008 self.instance_state(instance)
2009 .concurrent_state()
2010 .do_not_enter = true;
2011 }
2012
2013 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2017 log::trace!("exit {instance:?}");
2018 self.instance_state(instance)
2019 .concurrent_state()
2020 .do_not_enter = false;
2021 self.partition_pending(instance)
2022 }
2023
2024 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2032 for (thread, kind) in
2033 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2034 {
2035 let call = GuestCall { thread, kind };
2036 if call.is_ready(self)? {
2037 self.concurrent_state_mut()?
2038 .push_high_priority(WorkItem::GuestCall(instance.index, call));
2039 } else {
2040 self.instance_state(instance)
2041 .concurrent_state()
2042 .pending
2043 .insert(call.thread, call.kind);
2044 }
2045 }
2046
2047 if let Some(waker) = self
2048 .concurrent_state_mut()?
2049 .ready_for_concurrent_call_waker
2050 .take()
2051 {
2052 waker.wake();
2053 }
2054
2055 Ok(())
2056 }
2057
2058 pub(crate) fn backpressure_modify(
2060 &mut self,
2061 caller_instance: RuntimeInstance,
2062 modify: impl FnOnce(u16) -> Option<u16>,
2063 ) -> Result<()> {
2064 let state = self.instance_state(caller_instance).concurrent_state();
2065 let old = state.backpressure;
2066 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2067 state.backpressure = new;
2068
2069 if old > 0 && new == 0 {
2070 self.partition_pending(caller_instance)?;
2073 }
2074
2075 Ok(())
2076 }
2077
2078 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2081 let old_thread = self.current_thread()?;
2082 log::trace!("resume_fiber: save current thread {old_thread:?}");
2083
2084 let fiber = fiber::resolve_or_release(self, fiber).await?;
2085
2086 self.set_thread(old_thread)?;
2087
2088 let state = self.concurrent_state_mut()?;
2089
2090 if let Some(ot) = old_thread.guest() {
2091 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2092 }
2093 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2094
2095 if let Some(mut fiber) = fiber {
2096 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2097 let reason = match state.suspend_reason.take() {
2099 Some(r) => r,
2100 None => bail_bug!("suspend reason missing when resuming fiber"),
2101 };
2102 match reason {
2103 SuspendReason::NeedWork => {
2104 if state.worker.is_none() {
2105 state.worker = Some(fiber);
2106 } else {
2107 fiber.dispose(self);
2108 }
2109 }
2110 SuspendReason::Yielding {
2111 thread,
2112 cancellable,
2113 ..
2114 } => {
2115 state.get_mut(thread.thread)?.state =
2116 GuestThreadState::Ready { fiber, cancellable };
2117 let instance = state.get_mut(thread.task)?.instance.index;
2118 state.push_low_priority(WorkItem::ResumeThread(instance, thread));
2119 }
2120 SuspendReason::ExplicitlySuspending { thread, .. } => {
2121 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2122 }
2123 SuspendReason::Waiting { set, thread, .. } => {
2124 let old = state
2125 .get_mut(set)?
2126 .waiting
2127 .insert(thread, WaitMode::Fiber(fiber));
2128 assert!(old.is_none());
2129 }
2130 };
2131 } else {
2132 log::trace!("resume_fiber: fiber has exited");
2133 }
2134
2135 Ok(())
2136 }
2137
2138 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2144 log::trace!("suspend fiber: {reason:?}");
2145
2146 let task = match &reason {
2150 SuspendReason::Yielding { thread, .. }
2151 | SuspendReason::Waiting { thread, .. }
2152 | SuspendReason::ExplicitlySuspending { thread, .. } => Some(thread.task),
2153 SuspendReason::NeedWork => None,
2154 };
2155
2156 let old_guest_thread = if task.is_some() {
2157 self.current_thread()?
2158 } else {
2159 CurrentThread::None
2160 };
2161
2162 debug_assert!(
2168 matches!(
2169 reason,
2170 SuspendReason::ExplicitlySuspending {
2171 skip_may_block_check: true,
2172 ..
2173 } | SuspendReason::Waiting {
2174 skip_may_block_check: true,
2175 ..
2176 } | SuspendReason::Yielding {
2177 skip_may_block_check: true,
2178 ..
2179 }
2180 ) || old_guest_thread
2181 .guest()
2182 .map(|thread| self.concurrent_state_mut()?.may_block(thread.task))
2183 .transpose()?
2184 .unwrap_or(true)
2185 );
2186
2187 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2188 assert!(suspend_reason.is_none());
2189 *suspend_reason = Some(reason);
2190
2191 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2192
2193 if task.is_some() {
2194 self.set_thread(old_guest_thread)?;
2195 }
2196
2197 Ok(())
2198 }
2199
2200 fn wait_for_event(&mut self, waitable: Waitable) -> Result<()> {
2201 let caller = self.current_guest_thread()?;
2202 let state = self.concurrent_state_mut()?;
2203
2204 waitable.trap_if_in_waitable_set(state)?;
2205
2206 let set = state.get_mut(caller.thread)?.sync_call_set;
2207 waitable.join(state, Some(set))?;
2208 self.suspend(SuspendReason::Waiting {
2209 set,
2210 thread: caller,
2211 skip_may_block_check: false,
2212 })?;
2213 let state = self.concurrent_state_mut()?;
2214 waitable.join(state, None)
2215 }
2216
2217 fn cleanup_thread(
2239 &mut self,
2240 guest_thread: QualifiedThreadId,
2241 runtime_instance: RuntimeInstance,
2242 cleanup_task: CleanupTask,
2243 ) -> Result<()> {
2244 let state = self.concurrent_state_mut()?;
2245 let thread_data = state.get_mut(guest_thread.thread)?;
2246 let sync_call_set = thread_data.sync_call_set;
2247 if let Some(guest_id) = thread_data.instance_rep {
2248 self.instance_state(runtime_instance)
2249 .thread_handle_table()
2250 .guest_thread_remove(guest_id)?;
2251 }
2252 let state = self.concurrent_state_mut()?;
2253
2254 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2256 if let Some(Event::Subtask {
2257 status: Status::Returned | Status::ReturnCancelled,
2258 }) = waitable.common(state)?.event
2259 {
2260 waitable.delete_from(state)?;
2261 }
2262 }
2263
2264 state.delete(guest_thread.thread)?;
2265 state.delete(sync_call_set)?;
2266 let task = state.get_mut(guest_thread.task)?;
2267 task.threads.remove(&guest_thread.thread);
2268
2269 if task.threads.is_empty() && !task.returned_or_cancelled() {
2270 bail!(Trap::NoAsyncResult);
2271 }
2272 let ready_to_delete = task.ready_to_delete();
2273
2274 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2275 task.decremented_interesting_task_count = true;
2276
2277 debug_assert!(state.interesting_tasks > 0);
2278 state.interesting_tasks -= 1;
2279 if state.interesting_tasks == 0
2280 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2281 {
2282 waker.wake();
2283 }
2284 }
2285
2286 match cleanup_task {
2287 CleanupTask::Yes => {
2288 if ready_to_delete {
2289 Waitable::Guest(guest_thread.task).delete_from(state)?;
2290 }
2291 }
2292 CleanupTask::No => {}
2293 }
2294
2295 Ok(())
2296 }
2297
2298 fn cancel_guest_subtask_without_lowered_parameters(
2311 &mut self,
2312 caller_instance: RuntimeInstance,
2313 guest_task: TableId<GuestTask>,
2314 ) -> Result<()> {
2315 let concurrent_state = self.concurrent_state_mut()?;
2316 let task = concurrent_state.get_mut(guest_task)?;
2317 assert!(!task.already_lowered_parameters());
2318 task.lower_params = None;
2322 task.lift_result = None;
2323 task.exited = true;
2324 let instance = task.instance;
2325
2326 assert_eq!(1, task.threads.len());
2329 let thread = *task.threads.iter().next().unwrap();
2330 self.cleanup_thread(
2331 QualifiedThreadId {
2332 task: guest_task,
2333 thread,
2334 },
2335 caller_instance,
2336 CleanupTask::No,
2337 )?;
2338
2339 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2341 let pending_count = pending.len();
2342 pending.retain(|thread, _| thread.task != guest_task);
2343 if pending.len() == pending_count {
2345 bail!(Trap::SubtaskCancelAfterTerminal);
2346 }
2347 Ok(())
2348 }
2349
2350 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2353 if !self.concurrency_support() {
2354 return self.current_scope_id_not_concurrent();
2355 }
2356 let (bits, is_host) = match self.current_thread()? {
2357 CurrentThread::Guest(id) => (id.task.rep(), false),
2358 CurrentThread::Host(id) => (id.rep(), true),
2359 CurrentThread::None => return Ok(None),
2360 };
2361 assert_eq!((bits << 1) >> 1, bits);
2362 Ok(Some((bits << 1) | u32::from(is_host)))
2363 }
2364}
2365
2366enum CleanupTask {
2367 Yes,
2368 No,
2369}
2370
2371impl Instance {
2372 fn get_event(
2375 self,
2376 store: &mut StoreOpaque,
2377 guest_task: TableId<GuestTask>,
2378 set: Option<TableId<WaitableSet>>,
2379 cancellable: bool,
2380 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2381 let state = store.concurrent_state_mut()?;
2382
2383 let event = &mut state.get_mut(guest_task)?.event;
2384 if let Some(ev) = event
2385 && (cancellable || !matches!(ev, Event::Cancelled))
2386 {
2387 log::trace!("deliver event {ev:?} to {guest_task:?}");
2388 let ev = *ev;
2389 *event = None;
2390 return Ok(Some((ev, None)));
2391 }
2392
2393 let set = match set {
2394 Some(set) => set,
2395 None => return Ok(None),
2396 };
2397 let waitable = match state.get_mut(set)?.ready.pop_first() {
2398 Some(v) => v,
2399 None => return Ok(None),
2400 };
2401
2402 let common = waitable.common(state)?;
2403 let handle = match common.handle {
2404 Some(h) => h,
2405 None => bail_bug!("handle not set when delivering event"),
2406 };
2407 let event = match common.event.take() {
2408 Some(e) => e,
2409 None => bail_bug!("event not set when delivering event"),
2410 };
2411
2412 log::trace!(
2413 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2414 );
2415
2416 waitable.on_delivery(store, self, event)?;
2417
2418 Ok(Some((event, Some((waitable, handle)))))
2419 }
2420
2421 fn handle_callback_code(
2427 self,
2428 store: &mut StoreOpaque,
2429 guest_thread: QualifiedThreadId,
2430 runtime_instance: RuntimeComponentInstanceIndex,
2431 code: u32,
2432 ) -> Result<Option<GuestCall>> {
2433 let (code, set) = unpack_callback_code(code);
2434
2435 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2436
2437 let state = store.concurrent_state_mut()?;
2438
2439 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2440 let set = store
2441 .instance_state(self.runtime_instance(runtime_instance))
2442 .handle_table()
2443 .waitable_set_rep(handle)?;
2444
2445 Ok(TableId::<WaitableSet>::new(set))
2446 };
2447
2448 Ok(match code {
2449 callback_code::EXIT => {
2450 log::trace!("implicit thread {guest_thread:?} completed");
2451 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2452 task.exited = true;
2453 task.callback = None;
2454 store.cleanup_thread(
2455 guest_thread,
2456 self.runtime_instance(runtime_instance),
2457 CleanupTask::Yes,
2458 )?;
2459 None
2460 }
2461 callback_code::YIELD => {
2462 let task = state.get_mut(guest_thread.task)?;
2463 if let Some(event) = task.event {
2468 assert!(matches!(event, Event::None | Event::Cancelled));
2469 } else {
2470 task.event = Some(Event::None);
2471 }
2472 let call = GuestCall {
2473 thread: guest_thread,
2474 kind: GuestCallKind::DeliverEvent {
2475 instance: self,
2476 set: None,
2477 },
2478 };
2479 if state.may_block(guest_thread.task)? {
2480 state.push_low_priority(WorkItem::GuestCall(runtime_instance, call));
2483 None
2484 } else {
2485 Some(call)
2489 }
2490 }
2491 callback_code::WAIT => {
2492 state.check_blocking_for(guest_thread.task)?;
2495
2496 let set = get_set(store, set)?;
2497 let state = store.concurrent_state_mut()?;
2498
2499 if state.get_mut(guest_thread.task)?.event.is_some()
2500 || !state.get_mut(set)?.ready.is_empty()
2501 {
2502 state.push_high_priority(WorkItem::GuestCall(
2504 runtime_instance,
2505 GuestCall {
2506 thread: guest_thread,
2507 kind: GuestCallKind::DeliverEvent {
2508 instance: self,
2509 set: Some(set),
2510 },
2511 },
2512 ));
2513 } else {
2514 let old = state
2522 .get_mut(guest_thread.thread)?
2523 .wake_on_cancel
2524 .replace(set);
2525 if !old.is_none() {
2526 bail_bug!("thread unexpectedly had wake_on_cancel set");
2527 }
2528 let old = state
2529 .get_mut(set)?
2530 .waiting
2531 .insert(guest_thread, WaitMode::Callback(self));
2532 if !old.is_none() {
2533 bail_bug!("set's waiting set already had this thread registered");
2534 }
2535 }
2536 None
2537 }
2538 _ => bail!(Trap::UnsupportedCallbackCode),
2539 })
2540 }
2541
2542 unsafe fn queue_call<T: 'static>(
2549 self,
2550 mut store: StoreContextMut<T>,
2551 guest_thread: QualifiedThreadId,
2552 callee: SendSyncPtr<VMFuncRef>,
2553 param_count: usize,
2554 result_count: usize,
2555 async_: bool,
2556 callback: Option<SendSyncPtr<VMFuncRef>>,
2557 post_return: Option<SendSyncPtr<VMFuncRef>>,
2558 ) -> Result<()> {
2559 unsafe fn make_call<T: 'static>(
2574 store: StoreContextMut<T>,
2575 guest_thread: QualifiedThreadId,
2576 callee: SendSyncPtr<VMFuncRef>,
2577 param_count: usize,
2578 result_count: usize,
2579 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2580 + Send
2581 + Sync
2582 + 'static
2583 + use<T> {
2584 let token = StoreToken::new(store);
2585 move |store: &mut dyn VMStore| {
2586 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2587
2588 store
2589 .concurrent_state_mut()?
2590 .get_mut(guest_thread.thread)?
2591 .state = GuestThreadState::Running;
2592 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2593 let lower = match task.lower_params.take() {
2594 Some(l) => l,
2595 None => bail_bug!("lower_params missing"),
2596 };
2597
2598 lower(store, &mut storage[..param_count])?;
2599
2600 let mut store = token.as_context_mut(store);
2601
2602 unsafe {
2605 crate::Func::call_unchecked_raw(
2606 &mut store,
2607 callee.as_non_null(),
2608 NonNull::new(
2609 &mut storage[..param_count.max(result_count)]
2610 as *mut [MaybeUninit<ValRaw>] as _,
2611 )
2612 .unwrap(),
2613 )?;
2614 }
2615
2616 Ok(storage)
2617 }
2618 }
2619
2620 let call = unsafe {
2624 make_call(
2625 store.as_context_mut(),
2626 guest_thread,
2627 callee,
2628 param_count,
2629 result_count,
2630 )
2631 };
2632
2633 let callee_instance = store
2634 .0
2635 .concurrent_state_mut()?
2636 .get_mut(guest_thread.task)?
2637 .instance;
2638
2639 let fun = if callback.is_some() {
2640 assert!(async_);
2641
2642 Box::new(move |store: &mut dyn VMStore| {
2643 self.add_guest_thread_to_instance_table(
2644 guest_thread.thread,
2645 store,
2646 callee_instance.index,
2647 )?;
2648 let old_thread = store.set_thread(guest_thread)?;
2649 log::trace!(
2650 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2651 );
2652
2653 store.enter_instance(callee_instance);
2654
2655 let storage = call(store)?;
2662
2663 store.exit_instance(callee_instance)?;
2664
2665 store.set_thread(old_thread)?;
2666 let state = store.concurrent_state_mut()?;
2667 if let Some(t) = old_thread.guest() {
2668 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2669 }
2670 log::trace!("stackless call: restored {old_thread:?} as current thread");
2671
2672 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2675
2676 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2677 })
2678 as Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>
2679 } else {
2680 let token = StoreToken::new(store.as_context_mut());
2681 Box::new(move |store: &mut dyn VMStore| {
2682 self.add_guest_thread_to_instance_table(
2683 guest_thread.thread,
2684 store,
2685 callee_instance.index,
2686 )?;
2687 let old_thread = store.set_thread(guest_thread)?;
2688 log::trace!(
2689 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2690 );
2691 let flags = self.id().get(store).instance_flags(callee_instance.index);
2692
2693 if !async_ {
2697 store.enter_instance(callee_instance);
2698 }
2699
2700 let storage = call(store)?;
2707
2708 if !async_ {
2709 let lift = {
2715 store.exit_instance(callee_instance)?;
2716
2717 let state = store.concurrent_state_mut()?;
2718 if !state.get_mut(guest_thread.task)?.result.is_none() {
2719 bail_bug!("task has already produced a result");
2720 }
2721
2722 match state.get_mut(guest_thread.task)?.lift_result.take() {
2723 Some(lift) => lift,
2724 None => bail_bug!("lift_result field is missing"),
2725 }
2726 };
2727
2728 let result = (lift.lift)(store, unsafe {
2731 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2732 &storage[..result_count],
2733 )
2734 })?;
2735
2736 let post_return_arg = match result_count {
2737 0 => ValRaw::i32(0),
2738 1 => unsafe { storage[0].assume_init() },
2741 _ => unreachable!(),
2742 };
2743
2744 unsafe {
2745 call_post_return(
2746 token.as_context_mut(store),
2747 post_return.map(|v| v.as_non_null()),
2748 post_return_arg,
2749 flags,
2750 )?;
2751 }
2752
2753 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2754 }
2755
2756 store.set_thread(old_thread)?;
2757
2758 store
2759 .concurrent_state_mut()?
2760 .get_mut(guest_thread.task)?
2761 .exited = true;
2762
2763 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2765 Ok(None)
2766 })
2767 };
2768
2769 store
2770 .0
2771 .concurrent_state_mut()?
2772 .push_high_priority(WorkItem::GuestCall(
2773 callee_instance.index,
2774 GuestCall {
2775 thread: guest_thread,
2776 kind: GuestCallKind::StartImplicit(fun),
2777 },
2778 ));
2779
2780 Ok(())
2781 }
2782
2783 unsafe fn prepare_call<T: 'static>(
2796 self,
2797 mut store: StoreContextMut<T>,
2798 start: NonNull<VMFuncRef>,
2799 return_: NonNull<VMFuncRef>,
2800 caller_instance: RuntimeComponentInstanceIndex,
2801 callee_instance: RuntimeComponentInstanceIndex,
2802 task_return_type: TypeTupleIndex,
2803 callee_async: bool,
2804 memory: *mut VMMemoryDefinition,
2805 string_encoding: StringEncoding,
2806 caller_info: CallerInfo,
2807 ) -> Result<()> {
2808 if let (CallerInfo::Sync { .. }, true) = (&caller_info, callee_async) {
2809 store.0.check_blocking()?;
2813 }
2814
2815 enum ResultInfo {
2816 Heap { results: u32 },
2817 Stack { result_count: u32 },
2818 }
2819
2820 let result_info = match &caller_info {
2821 CallerInfo::Async {
2822 has_result: true,
2823 params,
2824 } => ResultInfo::Heap {
2825 results: match params.last() {
2826 Some(r) => r.get_u32(),
2827 None => bail_bug!("retptr missing"),
2828 },
2829 },
2830 CallerInfo::Async {
2831 has_result: false, ..
2832 } => ResultInfo::Stack { result_count: 0 },
2833 CallerInfo::Sync {
2834 result_count,
2835 params,
2836 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
2837 results: match params.last() {
2838 Some(r) => r.get_u32(),
2839 None => bail_bug!("arg ptr missing"),
2840 },
2841 },
2842 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
2843 result_count: *result_count,
2844 },
2845 };
2846
2847 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
2848
2849 let start = SendSyncPtr::new(start);
2853 let return_ = SendSyncPtr::new(return_);
2854 let token = StoreToken::new(store.as_context_mut());
2855 let old_thread = store.0.current_guest_thread()?;
2856 let state = store.0.concurrent_state_mut()?;
2857
2858 debug_assert_eq!(
2859 state.get_mut(old_thread.task)?.instance,
2860 self.runtime_instance(caller_instance)
2861 );
2862
2863 let guest_thread = GuestTask::new(
2864 state,
2865 Box::new(move |store, dst| {
2866 let mut store = token.as_context_mut(store);
2867 assert!(dst.len() <= MAX_FLAT_PARAMS);
2868 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
2870 let count = match caller_info {
2871 CallerInfo::Async { params, has_result } => {
2875 let params = ¶ms[..params.len() - usize::from(has_result)];
2876 for (param, src) in params.iter().zip(&mut src) {
2877 src.write(*param);
2878 }
2879 params.len()
2880 }
2881
2882 CallerInfo::Sync { params, .. } => {
2884 for (param, src) in params.iter().zip(&mut src) {
2885 src.write(*param);
2886 }
2887 params.len()
2888 }
2889 };
2890 unsafe {
2897 crate::Func::call_unchecked_raw(
2898 &mut store,
2899 start.as_non_null(),
2900 NonNull::new(
2901 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
2902 )
2903 .unwrap(),
2904 )?;
2905 }
2906 dst.copy_from_slice(&src[..dst.len()]);
2907 let task = store.0.current_guest_thread()?.task;
2908 let state = store.0.concurrent_state_mut()?;
2909 Waitable::Guest(task).set_event(
2910 state,
2911 Some(Event::Subtask {
2912 status: Status::Started,
2913 }),
2914 )?;
2915 Ok(())
2916 }),
2917 LiftResult {
2918 lift: Box::new(move |store, src| {
2919 let mut store = token.as_context_mut(store);
2922 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
2924 my_src.push(ValRaw::u32(*results));
2925 }
2926
2927 let prev = store.0.set_thread(old_thread)?;
2933
2934 unsafe {
2941 crate::Func::call_unchecked_raw(
2942 &mut store,
2943 return_.as_non_null(),
2944 my_src.as_mut_slice().into(),
2945 )?;
2946 }
2947
2948 store.0.set_thread(prev)?;
2951
2952 let thread = store.0.current_guest_thread()?;
2953 let state = store.0.concurrent_state_mut()?;
2954 if sync_caller {
2955 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
2956 if let ResultInfo::Stack { result_count } = &result_info {
2957 match result_count {
2958 0 => None,
2959 1 => Some(my_src[0]),
2960 _ => unreachable!(),
2961 }
2962 } else {
2963 None
2964 },
2965 );
2966 }
2967 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
2968 }),
2969 ty: task_return_type,
2970 memory: NonNull::new(memory).map(SendSyncPtr::new),
2971 string_encoding,
2972 },
2973 Caller::Guest { thread: old_thread },
2974 None,
2975 self.runtime_instance(callee_instance),
2976 callee_async,
2977 )?;
2978
2979 store.0.set_thread(guest_thread)?;
2982 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
2983
2984 Ok(())
2985 }
2986
2987 unsafe fn call_callback<T>(
2992 self,
2993 mut store: StoreContextMut<T>,
2994 function: SendSyncPtr<VMFuncRef>,
2995 event: Event,
2996 handle: u32,
2997 ) -> Result<u32> {
2998 let (ordinal, result) = event.parts();
2999 let params = &mut [
3000 ValRaw::u32(ordinal),
3001 ValRaw::u32(handle),
3002 ValRaw::u32(result),
3003 ];
3004 unsafe {
3009 crate::Func::call_unchecked_raw(
3010 &mut store,
3011 function.as_non_null(),
3012 params.as_mut_slice().into(),
3013 )?;
3014 }
3015 Ok(params[0].get_u32())
3016 }
3017
3018 unsafe fn start_call<T: 'static>(
3031 self,
3032 mut store: StoreContextMut<T>,
3033 callback: *mut VMFuncRef,
3034 post_return: *mut VMFuncRef,
3035 callee: NonNull<VMFuncRef>,
3036 param_count: u32,
3037 result_count: u32,
3038 flags: u32,
3039 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3040 ) -> Result<u32> {
3041 let token = StoreToken::new(store.as_context_mut());
3042 let async_caller = storage.is_none();
3043 let guest_thread = store.0.current_guest_thread()?;
3044 let state = store.0.concurrent_state_mut()?;
3045 let callee_async = state.get_mut(guest_thread.task)?.async_function;
3046 let callee = SendSyncPtr::new(callee);
3047 let param_count = usize::try_from(param_count)?;
3048 assert!(param_count <= MAX_FLAT_PARAMS);
3049 let result_count = usize::try_from(result_count)?;
3050 assert!(result_count <= MAX_FLAT_RESULTS);
3051
3052 let task = state.get_mut(guest_thread.task)?;
3053 if let Some(callback) = NonNull::new(callback) {
3054 let callback = SendSyncPtr::new(callback);
3058 task.callback = Some(Box::new(move |store, event, handle| {
3059 let store = token.as_context_mut(store);
3060 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3061 }));
3062 }
3063
3064 let Caller::Guest { thread: caller } = &task.caller else {
3065 bail_bug!("start_call unexpectedly invoked for host->guest call");
3068 };
3069 let caller = *caller;
3070 let caller_instance = state.get_mut(caller.task)?.instance;
3071
3072 unsafe {
3074 self.queue_call(
3075 store.as_context_mut(),
3076 guest_thread,
3077 callee,
3078 param_count,
3079 result_count,
3080 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3081 NonNull::new(callback).map(SendSyncPtr::new),
3082 NonNull::new(post_return).map(SendSyncPtr::new),
3083 )?;
3084 }
3085
3086 let state = store.0.concurrent_state_mut()?;
3087
3088 let guest_waitable = Waitable::Guest(guest_thread.task);
3091 let old_set = guest_waitable.common(state)?.set;
3092 let set = state.get_mut(caller.thread)?.sync_call_set;
3093 guest_waitable.join(state, Some(set))?;
3094
3095 store.0.set_thread(CurrentThread::None)?;
3096
3097 let (status, waitable) = loop {
3113 store.0.suspend(SuspendReason::Waiting {
3114 set,
3115 thread: caller,
3116 skip_may_block_check: async_caller || !callee_async,
3124 })?;
3125
3126 let state = store.0.concurrent_state_mut()?;
3127
3128 log::trace!("taking event for {:?}", guest_thread.task);
3129 let event = guest_waitable.take_event(state)?;
3130 let Some(Event::Subtask { status }) = event else {
3131 bail_bug!("subtasks should only get subtask events, got {event:?}")
3132 };
3133
3134 log::trace!("status {status:?} for {:?}", guest_thread.task);
3135
3136 if status == Status::Returned {
3137 break (status, None);
3139 } else if async_caller {
3140 let handle = store
3144 .0
3145 .instance_state(caller_instance)
3146 .handle_table()
3147 .subtask_insert_guest(guest_thread.task.rep())?;
3148 store
3149 .0
3150 .concurrent_state_mut()?
3151 .get_mut(guest_thread.task)?
3152 .common
3153 .handle = Some(handle);
3154 break (status, Some(handle));
3155 } else {
3156 }
3160 };
3161
3162 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3163
3164 store.0.set_thread(caller)?;
3166 store
3167 .0
3168 .concurrent_state_mut()?
3169 .get_mut(caller.thread)?
3170 .state = GuestThreadState::Running;
3171 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3172
3173 if let Some(storage) = storage {
3174 let state = store.0.concurrent_state_mut()?;
3178 let task = state.get_mut(guest_thread.task)?;
3179 if let Some(result) = task.sync_result.take()? {
3180 if let Some(result) = result {
3181 storage[0] = MaybeUninit::new(result);
3182 }
3183
3184 if task.exited && task.ready_to_delete() {
3185 Waitable::Guest(guest_thread.task).delete_from(state)?;
3186 }
3187 }
3188 }
3189
3190 Ok(status.pack(waitable))
3191 }
3192
3193 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3206 self,
3207 mut store: StoreContextMut<'_, T>,
3208 future: impl Future<Output = Result<R>> + Send + 'static,
3209 lower: impl FnOnce(StoreContextMut<T>, Option<R>) -> Result<()> + Send + 'static,
3210 ) -> Result<Option<u32>> {
3211 let token = StoreToken::new(store.as_context_mut());
3212 let task = store.0.current_host_thread()?;
3213 let state = store.0.concurrent_state_mut()?;
3214
3215 let (join_handle, future) = JoinHandle::run(future);
3218 {
3219 let state = &mut state.get_mut(task)?.state;
3220 assert!(matches!(state, HostTaskState::CalleeStarted));
3221 *state = HostTaskState::CalleeRunning(join_handle);
3222 }
3223
3224 let mut future = Box::pin(future);
3225
3226 let poll = tls::set(store.0, || {
3231 future
3232 .as_mut()
3233 .poll(&mut Context::from_waker(&Waker::noop()))
3234 });
3235
3236 match poll {
3237 Poll::Ready(result) => {
3239 let result = result.transpose()?;
3240 lower(store.as_context_mut(), result)?;
3241 return Ok(None);
3242 }
3243
3244 Poll::Pending => {}
3246 }
3247
3248 let future = Box::pin(async move {
3256 let result = match future.await {
3257 Some(result) => Some(result?),
3258 None => None,
3259 };
3260 let on_complete = move |store: &mut dyn VMStore| {
3261 let mut store = token.as_context_mut(store);
3265 let old = store.0.set_thread(task)?;
3266
3267 let status = if result.is_some() {
3268 Status::Returned
3269 } else {
3270 Status::ReturnCancelled
3271 };
3272
3273 lower(store.as_context_mut(), result)?;
3274 let state = store.0.concurrent_state_mut()?;
3275 match &mut state.get_mut(task)?.state {
3276 HostTaskState::CalleeDone { .. } => {}
3279
3280 other => *other = HostTaskState::CalleeDone { cancelled: false },
3282 }
3283 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3284
3285 store.0.set_thread(old)?;
3286 Ok(())
3287 };
3288
3289 tls::get(move |store| {
3294 store
3295 .concurrent_state_mut()?
3296 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3297 on_complete,
3298 ))));
3299 Ok(())
3300 })
3301 });
3302
3303 let state = store.0.concurrent_state_mut()?;
3306 state.push_future(future);
3307 let caller = state.get_mut(task)?.caller;
3308 let instance = state.get_mut(caller.task)?.instance;
3309 let handle = store
3310 .0
3311 .instance_state(instance)
3312 .handle_table()
3313 .subtask_insert_host(task.rep())?;
3314 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3315 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3316
3317 store.0.set_thread(caller)?;
3321 Ok(Some(handle))
3322 }
3323
3324 pub(crate) fn task_return(
3327 self,
3328 store: &mut dyn VMStore,
3329 ty: TypeTupleIndex,
3330 options: OptionsIndex,
3331 storage: &[ValRaw],
3332 ) -> Result<()> {
3333 let guest_thread = store.current_guest_thread()?;
3334 let state = store.concurrent_state_mut()?;
3335 let lift = state
3336 .get_mut(guest_thread.task)?
3337 .lift_result
3338 .take()
3339 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3340 if !state.get_mut(guest_thread.task)?.result.is_none() {
3341 bail_bug!("task result unexpectedly already set");
3342 }
3343
3344 let CanonicalOptions {
3345 string_encoding,
3346 data_model,
3347 ..
3348 } = &self.id().get(store).component().env_component().options[options];
3349
3350 let invalid = ty != lift.ty
3351 || string_encoding != &lift.string_encoding
3352 || match data_model {
3353 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3354 Some(memory) => {
3355 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3356 let actual = self.id().get(store).runtime_memory(memory);
3357 expected != actual.as_ptr()
3358 }
3359 None => false,
3362 },
3363 CanonicalOptionsDataModel::Gc { .. } => true,
3365 };
3366
3367 if invalid {
3368 bail!(Trap::TaskReturnInvalid);
3369 }
3370
3371 log::trace!("task.return for {guest_thread:?}");
3372
3373 let result = (lift.lift)(store, storage)?;
3374 self.task_complete(store, guest_thread.task, result, Status::Returned)
3375 }
3376
3377 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3379 let guest_thread = store.current_guest_thread()?;
3380 let state = store.concurrent_state_mut()?;
3381 let task = state.get_mut(guest_thread.task)?;
3382 if !task.cancel_sent {
3383 bail!(Trap::TaskCancelNotCancelled);
3384 }
3385 _ = task
3386 .lift_result
3387 .take()
3388 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3389
3390 if !task.result.is_none() {
3391 bail_bug!("task result should not bet set yet");
3392 }
3393
3394 log::trace!("task.cancel for {guest_thread:?}");
3395
3396 self.task_complete(
3397 store,
3398 guest_thread.task,
3399 Box::new(DummyResult),
3400 Status::ReturnCancelled,
3401 )
3402 }
3403
3404 fn task_complete(
3410 self,
3411 store: &mut StoreOpaque,
3412 guest_task: TableId<GuestTask>,
3413 result: Box<dyn Any + Send + Sync>,
3414 status: Status,
3415 ) -> Result<()> {
3416 store
3417 .component_resource_tables(Some(self))?
3418 .validate_scope_exit()?;
3419
3420 let state = store.concurrent_state_mut()?;
3421 let task = state.get_mut(guest_task)?;
3422
3423 if let Caller::Host { tx, .. } = &mut task.caller {
3424 if let Some(tx) = tx.take() {
3425 _ = tx.send(result);
3426 }
3427 } else {
3428 task.result = Some(result);
3429 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3430 }
3431
3432 Ok(())
3433 }
3434
3435 pub(crate) fn waitable_set_new(
3437 self,
3438 store: &mut StoreOpaque,
3439 caller_instance: RuntimeComponentInstanceIndex,
3440 ) -> Result<u32> {
3441 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3442 let handle = store
3443 .instance_state(self.runtime_instance(caller_instance))
3444 .handle_table()
3445 .waitable_set_insert(set.rep())?;
3446 log::trace!("new waitable set {set:?} (handle {handle})");
3447 Ok(handle)
3448 }
3449
3450 pub(crate) fn waitable_set_drop(
3452 self,
3453 store: &mut StoreOpaque,
3454 caller_instance: RuntimeComponentInstanceIndex,
3455 set: u32,
3456 ) -> Result<()> {
3457 let rep = store
3458 .instance_state(self.runtime_instance(caller_instance))
3459 .handle_table()
3460 .waitable_set_remove(set)?;
3461
3462 log::trace!("drop waitable set {rep} (handle {set})");
3463
3464 if !store
3468 .concurrent_state_mut()?
3469 .get_mut(TableId::<WaitableSet>::new(rep))?
3470 .waiting
3471 .is_empty()
3472 {
3473 bail!(Trap::WaitableSetDropHasWaiters);
3474 }
3475
3476 store
3477 .concurrent_state_mut()?
3478 .delete(TableId::<WaitableSet>::new(rep))?;
3479
3480 Ok(())
3481 }
3482
3483 pub(crate) fn waitable_join(
3485 self,
3486 store: &mut StoreOpaque,
3487 caller_instance: RuntimeComponentInstanceIndex,
3488 waitable_handle: u32,
3489 set_handle: u32,
3490 ) -> Result<()> {
3491 let mut instance = self.id().get_mut(store);
3492 let waitable =
3493 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3494
3495 let set = if set_handle == 0 {
3496 None
3497 } else {
3498 let set = instance.instance_states().0[caller_instance]
3499 .handle_table()
3500 .waitable_set_rep(set_handle)?;
3501
3502 let state = store.concurrent_state_mut()?;
3503 if let Some(old) = waitable.common(state)?.set
3504 && state.get_mut(old)?.is_sync_call_set
3505 {
3506 bail!(Trap::WaitableSyncAndAsync);
3507 }
3508
3509 Some(TableId::<WaitableSet>::new(set))
3510 };
3511
3512 log::trace!(
3513 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3514 );
3515
3516 waitable.join(store.concurrent_state_mut()?, set)
3517 }
3518
3519 pub(crate) fn subtask_drop(
3521 self,
3522 store: &mut StoreOpaque,
3523 caller_instance: RuntimeComponentInstanceIndex,
3524 task_id: u32,
3525 ) -> Result<()> {
3526 self.waitable_join(store, caller_instance, task_id, 0)?;
3527
3528 let (rep, is_host) = store
3529 .instance_state(self.runtime_instance(caller_instance))
3530 .handle_table()
3531 .subtask_remove(task_id)?;
3532
3533 let concurrent_state = store.concurrent_state_mut()?;
3534 let (waitable, delete) = if is_host {
3535 let id = TableId::<HostTask>::new(rep);
3536 let task = concurrent_state.get_mut(id)?;
3537 match &task.state {
3538 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3539 HostTaskState::CalleeDone { .. } => {}
3540 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3541 bail_bug!("invalid state for callee in `subtask.drop`")
3542 }
3543 }
3544 (Waitable::Host(id), true)
3545 } else {
3546 let id = TableId::<GuestTask>::new(rep);
3547 let task = concurrent_state.get_mut(id)?;
3548 if task.lift_result.is_some() {
3549 bail!(Trap::SubtaskDropNotResolved);
3550 }
3551 (
3552 Waitable::Guest(id),
3553 concurrent_state.get_mut(id)?.ready_to_delete(),
3554 )
3555 };
3556
3557 waitable.common(concurrent_state)?.handle = None;
3558
3559 if waitable.take_event(concurrent_state)?.is_some() {
3562 bail!(Trap::SubtaskDropNotResolved);
3563 }
3564
3565 if delete {
3566 waitable.delete_from(concurrent_state)?;
3567 }
3568
3569 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3570 Ok(())
3571 }
3572
3573 pub(crate) fn waitable_set_wait(
3575 self,
3576 store: &mut StoreOpaque,
3577 options: OptionsIndex,
3578 set: u32,
3579 payload: u32,
3580 ) -> Result<u32> {
3581 if !self.options(store, options).async_ {
3582 store.check_blocking()?;
3586 }
3587
3588 let &CanonicalOptions {
3589 cancellable,
3590 instance: caller_instance,
3591 ..
3592 } = &self.id().get(store).component().env_component().options[options];
3593 let rep = store
3594 .instance_state(self.runtime_instance(caller_instance))
3595 .handle_table()
3596 .waitable_set_rep(set)?;
3597
3598 self.waitable_check(
3599 store,
3600 cancellable,
3601 WaitableCheck::Wait,
3602 WaitableCheckParams {
3603 set: TableId::new(rep),
3604 options,
3605 payload,
3606 },
3607 )
3608 }
3609
3610 pub(crate) fn waitable_set_poll(
3612 self,
3613 store: &mut StoreOpaque,
3614 options: OptionsIndex,
3615 set: u32,
3616 payload: u32,
3617 ) -> Result<u32> {
3618 let &CanonicalOptions {
3619 cancellable,
3620 instance: caller_instance,
3621 ..
3622 } = &self.id().get(store).component().env_component().options[options];
3623 let rep = store
3624 .instance_state(self.runtime_instance(caller_instance))
3625 .handle_table()
3626 .waitable_set_rep(set)?;
3627
3628 self.waitable_check(
3629 store,
3630 cancellable,
3631 WaitableCheck::Poll,
3632 WaitableCheckParams {
3633 set: TableId::new(rep),
3634 options,
3635 payload,
3636 },
3637 )
3638 }
3639
3640 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3642 let thread_id = store.current_guest_thread()?.thread;
3643 match store
3644 .concurrent_state_mut()?
3645 .get_mut(thread_id)?
3646 .instance_rep
3647 {
3648 Some(r) => Ok(r),
3649 None => bail_bug!("thread should have instance_rep by now"),
3650 }
3651 }
3652
3653 pub(crate) fn thread_new_indirect<T: 'static>(
3655 self,
3656 mut store: StoreContextMut<T>,
3657 runtime_instance: RuntimeComponentInstanceIndex,
3658 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3660 start_func_idx: u32,
3661 context: i32,
3662 ) -> Result<u32> {
3663 log::trace!("creating new thread");
3664
3665 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3666 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3667 let callee = instance
3668 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3669 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3670 if callee.type_index(store.0) != start_func_ty.type_index() {
3671 bail!(Trap::ThreadNewIndirectInvalidType);
3672 }
3673
3674 let token = StoreToken::new(store.as_context_mut());
3675 let start_func = Box::new(
3676 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3677 let old_thread = store.set_thread(guest_thread)?;
3678 log::trace!(
3679 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3680 );
3681
3682 let mut store = token.as_context_mut(store);
3683 let mut params = [ValRaw::i32(context)];
3684 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3687
3688 store.0.set_thread(old_thread)?;
3689
3690 store.0.cleanup_thread(
3691 guest_thread,
3692 self.runtime_instance(runtime_instance),
3693 CleanupTask::Yes,
3694 )?;
3695 log::trace!("explicit thread {guest_thread:?} completed");
3696 let state = store.0.concurrent_state_mut()?;
3697 if let Some(t) = old_thread.guest() {
3698 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3699 }
3700 log::trace!("thread start: restored {old_thread:?} as current thread");
3701
3702 Ok(())
3703 },
3704 );
3705
3706 let current_thread = store.0.current_guest_thread()?;
3707 let state = store.0.concurrent_state_mut()?;
3708 let parent_task = current_thread.task;
3709
3710 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3711 let thread_id = state.push(new_thread)?;
3712 state.get_mut(parent_task)?.threads.insert(thread_id);
3713
3714 log::trace!("new thread with id {thread_id:?} created");
3715
3716 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3717 }
3718
3719 pub(crate) fn resume_thread(
3720 self,
3721 store: &mut StoreOpaque,
3722 runtime_instance: RuntimeComponentInstanceIndex,
3723 thread_idx: u32,
3724 high_priority: bool,
3725 allow_ready: bool,
3726 ) -> Result<()> {
3727 let thread_id =
3728 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3729 let state = store.concurrent_state_mut()?;
3730 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3731 let thread = state.get_mut(guest_thread.thread)?;
3732
3733 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3734 GuestThreadState::NotStartedExplicit(start_func) => {
3735 log::trace!("starting thread {guest_thread:?}");
3736 let guest_call = WorkItem::GuestCall(
3737 runtime_instance,
3738 GuestCall {
3739 thread: guest_thread,
3740 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3741 start_func(store, guest_thread)
3742 })),
3743 },
3744 );
3745 store
3746 .concurrent_state_mut()?
3747 .push_work_item(guest_call, high_priority);
3748 }
3749 GuestThreadState::Suspended(fiber) => {
3750 log::trace!("resuming thread {thread_id:?} that was suspended");
3751 store
3752 .concurrent_state_mut()?
3753 .push_work_item(WorkItem::ResumeFiber(fiber), high_priority);
3754 }
3755 GuestThreadState::Ready { fiber, cancellable } if allow_ready => {
3756 log::trace!("resuming thread {thread_id:?} that was ready");
3757 thread.state = GuestThreadState::Ready { fiber, cancellable };
3758 store
3759 .concurrent_state_mut()?
3760 .promote_thread_work_item(guest_thread);
3761 }
3762 other => {
3763 thread.state = other;
3764 bail!(Trap::CannotResumeThread);
3765 }
3766 }
3767 Ok(())
3768 }
3769
3770 fn add_guest_thread_to_instance_table(
3771 self,
3772 thread_id: TableId<GuestThread>,
3773 store: &mut StoreOpaque,
3774 runtime_instance: RuntimeComponentInstanceIndex,
3775 ) -> Result<u32> {
3776 let guest_id = store
3777 .instance_state(self.runtime_instance(runtime_instance))
3778 .thread_handle_table()
3779 .guest_thread_insert(thread_id.rep())?;
3780 store
3781 .concurrent_state_mut()?
3782 .get_mut(thread_id)?
3783 .instance_rep = Some(guest_id);
3784 Ok(guest_id)
3785 }
3786
3787 pub(crate) fn suspension_intrinsic(
3790 self,
3791 store: &mut StoreOpaque,
3792 caller: RuntimeComponentInstanceIndex,
3793 cancellable: bool,
3794 yielding: bool,
3795 to_thread: SuspensionTarget,
3796 ) -> Result<WaitResult> {
3797 let guest_thread = store.current_guest_thread()?;
3798 if to_thread.is_none() {
3799 let state = store.concurrent_state_mut()?;
3800 if yielding {
3801 if !state.may_block(guest_thread.task)? {
3803 if !state.promote_instance_local_thread_work_item(caller) {
3806 return Ok(WaitResult::Completed);
3808 }
3809 }
3810 } else {
3811 store.check_blocking()?;
3815 }
3816 }
3817
3818 if cancellable && store.take_pending_cancellation()? {
3820 return Ok(WaitResult::Cancelled);
3821 }
3822
3823 match to_thread {
3824 SuspensionTarget::SomeSuspended(thread) => {
3825 self.resume_thread(store, caller, thread, true, false)?
3826 }
3827 SuspensionTarget::Some(thread) => {
3828 self.resume_thread(store, caller, thread, true, true)?
3829 }
3830 SuspensionTarget::None => { }
3831 }
3832
3833 let reason = if yielding {
3834 SuspendReason::Yielding {
3835 thread: guest_thread,
3836 cancellable,
3837 skip_may_block_check: to_thread.is_some(),
3841 }
3842 } else {
3843 SuspendReason::ExplicitlySuspending {
3844 thread: guest_thread,
3845 skip_may_block_check: to_thread.is_some(),
3849 }
3850 };
3851
3852 store.suspend(reason)?;
3853
3854 if cancellable && store.take_pending_cancellation()? {
3855 Ok(WaitResult::Cancelled)
3856 } else {
3857 Ok(WaitResult::Completed)
3858 }
3859 }
3860
3861 fn waitable_check(
3863 self,
3864 store: &mut StoreOpaque,
3865 cancellable: bool,
3866 check: WaitableCheck,
3867 params: WaitableCheckParams,
3868 ) -> Result<u32> {
3869 let guest_thread = store.current_guest_thread()?;
3870
3871 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
3872
3873 let state = store.concurrent_state_mut()?;
3874 let task = state.get_mut(guest_thread.task)?;
3875
3876 match &check {
3879 WaitableCheck::Wait => {
3880 let set = params.set;
3881
3882 if (task.event.is_none()
3883 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
3884 && state.get_mut(set)?.ready.is_empty()
3885 {
3886 if cancellable {
3887 let old = state
3888 .get_mut(guest_thread.thread)?
3889 .wake_on_cancel
3890 .replace(set);
3891 if !old.is_none() {
3892 bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
3893 }
3894 }
3895
3896 store.suspend(SuspendReason::Waiting {
3897 set,
3898 thread: guest_thread,
3899 skip_may_block_check: false,
3900 })?;
3901 }
3902 }
3903 WaitableCheck::Poll => {}
3904 }
3905
3906 log::trace!(
3907 "waitable check for {guest_thread:?}; set {:?}, part two",
3908 params.set
3909 );
3910
3911 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
3913
3914 let (ordinal, handle, result) = match &check {
3915 WaitableCheck::Wait => {
3916 let (event, waitable) = match event {
3917 Some(p) => p,
3918 None => bail_bug!("event expected to be present"),
3919 };
3920 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3921 let (ordinal, result) = event.parts();
3922 (ordinal, handle, result)
3923 }
3924 WaitableCheck::Poll => {
3925 if let Some((event, waitable)) = event {
3926 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3927 let (ordinal, result) = event.parts();
3928 (ordinal, handle, result)
3929 } else {
3930 log::trace!(
3931 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
3932 guest_thread.task,
3933 params.set
3934 );
3935 let (ordinal, result) = Event::None.parts();
3936 (ordinal, 0, result)
3937 }
3938 }
3939 };
3940 let memory = self.options_memory_mut(store, params.options);
3941 let ptr = crate::component::func::validate_inbounds_dynamic(
3942 &CanonicalAbiInfo::POINTER_PAIR,
3943 memory,
3944 &ValRaw::u32(params.payload),
3945 )?;
3946 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
3947 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
3948 Ok(ordinal)
3949 }
3950
3951 pub(crate) fn subtask_cancel(
3953 self,
3954 store: &mut StoreOpaque,
3955 caller_instance: RuntimeComponentInstanceIndex,
3956 async_: bool,
3957 task_id: u32,
3958 ) -> Result<u32> {
3959 if !async_ {
3960 store.check_blocking()?;
3964 }
3965
3966 let (rep, is_host) = store
3967 .instance_state(self.runtime_instance(caller_instance))
3968 .handle_table()
3969 .subtask_rep(task_id)?;
3970 let waitable = if is_host {
3971 Waitable::Host(TableId::<HostTask>::new(rep))
3972 } else {
3973 Waitable::Guest(TableId::<GuestTask>::new(rep))
3974 };
3975 let concurrent_state = store.concurrent_state_mut()?;
3976
3977 log::trace!("subtask_cancel {waitable:?} (handle {task_id})");
3978
3979 if !async_ {
3980 waitable.trap_if_in_waitable_set(concurrent_state)?;
3981 }
3982
3983 let needs_block;
3984 if let Waitable::Host(host_task) = waitable {
3985 let state = &mut concurrent_state.get_mut(host_task)?.state;
3986 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
3987 HostTaskState::CalleeRunning(handle) => {
3994 handle.abort();
3995 needs_block = true;
3996 }
3997
3998 HostTaskState::CalleeDone { cancelled } => {
4001 if cancelled {
4002 bail!(Trap::SubtaskCancelAfterTerminal);
4003 } else {
4004 needs_block = false;
4007 }
4008 }
4009
4010 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4013 bail_bug!("invalid states for host callee")
4014 }
4015 }
4016 } else {
4017 let guest_task = TableId::<GuestTask>::new(rep);
4018 let task = concurrent_state.get_mut(guest_task)?;
4019 if !task.already_lowered_parameters() {
4020 store.cancel_guest_subtask_without_lowered_parameters(
4021 self.runtime_instance(caller_instance),
4022 guest_task,
4023 )?;
4024 return Ok(Status::StartCancelled as u32);
4025 } else if !task.returned_or_cancelled() {
4026 task.cancel_sent = true;
4029 task.event = Some(Event::Cancelled);
4034 let runtime_instance = task.instance.index;
4035 for thread in task.threads.clone() {
4036 let thread = QualifiedThreadId {
4037 task: guest_task,
4038 thread,
4039 };
4040 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4041 if let Some(set) = thread_mut.wake_on_cancel.take() {
4042 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4044 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber(fiber),
4045 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall(
4046 runtime_instance,
4047 GuestCall {
4048 thread,
4049 kind: GuestCallKind::DeliverEvent {
4050 instance,
4051 set: None,
4052 },
4053 },
4054 ),
4055 None => bail_bug!("thread not present in wake_on_cancel set"),
4056 };
4057 concurrent_state.push_high_priority(item);
4058
4059 let caller = store.current_guest_thread()?;
4060 store.suspend(SuspendReason::Yielding {
4061 thread: caller,
4062 cancellable: false,
4063 skip_may_block_check: false,
4066 })?;
4067 break;
4068 } else if let GuestThreadState::Ready {
4069 cancellable: true, ..
4070 } = &thread_mut.state
4071 {
4072 concurrent_state.promote_thread_work_item(thread);
4075 let caller = store.current_guest_thread()?;
4076 store.suspend(SuspendReason::Yielding {
4077 thread: caller,
4078 cancellable: false,
4079 skip_may_block_check: false,
4080 })?;
4081 break;
4082 }
4083 }
4084
4085 needs_block = !store
4088 .concurrent_state_mut()?
4089 .get_mut(guest_task)?
4090 .returned_or_cancelled()
4091 } else {
4092 needs_block = false;
4093 }
4094 };
4095
4096 if needs_block {
4100 if async_ {
4101 return Ok(BLOCKED);
4102 }
4103
4104 store.wait_for_event(waitable)?;
4108
4109 }
4111
4112 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4113 if let Some(Event::Subtask {
4114 status: status @ (Status::Returned | Status::ReturnCancelled),
4115 }) = event
4116 {
4117 Ok(status as u32)
4118 } else {
4119 bail!(Trap::SubtaskCancelAfterTerminal);
4120 }
4121 }
4122}
4123
4124pub trait VMComponentAsyncStore {
4132 unsafe fn prepare_call(
4138 &mut self,
4139 instance: Instance,
4140 memory: *mut VMMemoryDefinition,
4141 start: NonNull<VMFuncRef>,
4142 return_: NonNull<VMFuncRef>,
4143 caller_instance: RuntimeComponentInstanceIndex,
4144 callee_instance: RuntimeComponentInstanceIndex,
4145 task_return_type: TypeTupleIndex,
4146 callee_async: bool,
4147 string_encoding: StringEncoding,
4148 result_count: u32,
4149 storage: *mut ValRaw,
4150 storage_len: usize,
4151 ) -> Result<()>;
4152
4153 unsafe fn sync_start(
4156 &mut self,
4157 instance: Instance,
4158 callback: *mut VMFuncRef,
4159 callee: NonNull<VMFuncRef>,
4160 param_count: u32,
4161 storage: *mut MaybeUninit<ValRaw>,
4162 storage_len: usize,
4163 ) -> Result<()>;
4164
4165 unsafe fn async_start(
4168 &mut self,
4169 instance: Instance,
4170 callback: *mut VMFuncRef,
4171 post_return: *mut VMFuncRef,
4172 callee: NonNull<VMFuncRef>,
4173 param_count: u32,
4174 result_count: u32,
4175 flags: u32,
4176 ) -> Result<u32>;
4177
4178 fn future_write(
4180 &mut self,
4181 instance: Instance,
4182 caller: RuntimeComponentInstanceIndex,
4183 ty: TypeFutureTableIndex,
4184 options: OptionsIndex,
4185 future: u32,
4186 address: u32,
4187 ) -> Result<u32>;
4188
4189 fn future_read(
4191 &mut self,
4192 instance: Instance,
4193 caller: RuntimeComponentInstanceIndex,
4194 ty: TypeFutureTableIndex,
4195 options: OptionsIndex,
4196 future: u32,
4197 address: u32,
4198 ) -> Result<u32>;
4199
4200 fn future_drop_writable(
4202 &mut self,
4203 instance: Instance,
4204 ty: TypeFutureTableIndex,
4205 writer: u32,
4206 ) -> Result<()>;
4207
4208 fn stream_write(
4210 &mut self,
4211 instance: Instance,
4212 caller: RuntimeComponentInstanceIndex,
4213 ty: TypeStreamTableIndex,
4214 options: OptionsIndex,
4215 stream: u32,
4216 address: u32,
4217 count: u32,
4218 ) -> Result<u32>;
4219
4220 fn stream_read(
4222 &mut self,
4223 instance: Instance,
4224 caller: RuntimeComponentInstanceIndex,
4225 ty: TypeStreamTableIndex,
4226 options: OptionsIndex,
4227 stream: u32,
4228 address: u32,
4229 count: u32,
4230 ) -> Result<u32>;
4231
4232 fn flat_stream_write(
4235 &mut self,
4236 instance: Instance,
4237 caller: RuntimeComponentInstanceIndex,
4238 ty: TypeStreamTableIndex,
4239 options: OptionsIndex,
4240 payload_size: u32,
4241 payload_align: u32,
4242 stream: u32,
4243 address: u32,
4244 count: u32,
4245 ) -> Result<u32>;
4246
4247 fn flat_stream_read(
4250 &mut self,
4251 instance: Instance,
4252 caller: RuntimeComponentInstanceIndex,
4253 ty: TypeStreamTableIndex,
4254 options: OptionsIndex,
4255 payload_size: u32,
4256 payload_align: u32,
4257 stream: u32,
4258 address: u32,
4259 count: u32,
4260 ) -> Result<u32>;
4261
4262 fn stream_drop_writable(
4264 &mut self,
4265 instance: Instance,
4266 ty: TypeStreamTableIndex,
4267 writer: u32,
4268 ) -> Result<()>;
4269
4270 fn error_context_debug_message(
4272 &mut self,
4273 instance: Instance,
4274 ty: TypeComponentLocalErrorContextTableIndex,
4275 options: OptionsIndex,
4276 err_ctx_handle: u32,
4277 debug_msg_address: u32,
4278 ) -> Result<()>;
4279
4280 fn thread_new_indirect(
4282 &mut self,
4283 instance: Instance,
4284 caller: RuntimeComponentInstanceIndex,
4285 func_ty_idx: TypeFuncIndex,
4286 start_func_table_idx: RuntimeTableIndex,
4287 start_func_idx: u32,
4288 context: i32,
4289 ) -> Result<u32>;
4290}
4291
4292impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4294 unsafe fn prepare_call(
4295 &mut self,
4296 instance: Instance,
4297 memory: *mut VMMemoryDefinition,
4298 start: NonNull<VMFuncRef>,
4299 return_: NonNull<VMFuncRef>,
4300 caller_instance: RuntimeComponentInstanceIndex,
4301 callee_instance: RuntimeComponentInstanceIndex,
4302 task_return_type: TypeTupleIndex,
4303 callee_async: bool,
4304 string_encoding: StringEncoding,
4305 result_count_or_max_if_async: u32,
4306 storage: *mut ValRaw,
4307 storage_len: usize,
4308 ) -> Result<()> {
4309 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4313
4314 unsafe {
4315 instance.prepare_call(
4316 StoreContextMut(self),
4317 start,
4318 return_,
4319 caller_instance,
4320 callee_instance,
4321 task_return_type,
4322 callee_async,
4323 memory,
4324 string_encoding,
4325 match result_count_or_max_if_async {
4326 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4327 params,
4328 has_result: false,
4329 },
4330 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4331 params,
4332 has_result: true,
4333 },
4334 result_count => CallerInfo::Sync {
4335 params,
4336 result_count,
4337 },
4338 },
4339 )
4340 }
4341 }
4342
4343 unsafe fn sync_start(
4344 &mut self,
4345 instance: Instance,
4346 callback: *mut VMFuncRef,
4347 callee: NonNull<VMFuncRef>,
4348 param_count: u32,
4349 storage: *mut MaybeUninit<ValRaw>,
4350 storage_len: usize,
4351 ) -> Result<()> {
4352 unsafe {
4353 instance
4354 .start_call(
4355 StoreContextMut(self),
4356 callback,
4357 ptr::null_mut(),
4358 callee,
4359 param_count,
4360 1,
4361 START_FLAG_ASYNC_CALLEE,
4362 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4366 )
4367 .map(drop)
4368 }
4369 }
4370
4371 unsafe fn async_start(
4372 &mut self,
4373 instance: Instance,
4374 callback: *mut VMFuncRef,
4375 post_return: *mut VMFuncRef,
4376 callee: NonNull<VMFuncRef>,
4377 param_count: u32,
4378 result_count: u32,
4379 flags: u32,
4380 ) -> Result<u32> {
4381 unsafe {
4382 instance.start_call(
4383 StoreContextMut(self),
4384 callback,
4385 post_return,
4386 callee,
4387 param_count,
4388 result_count,
4389 flags,
4390 None,
4391 )
4392 }
4393 }
4394
4395 fn future_write(
4396 &mut self,
4397 instance: Instance,
4398 caller: RuntimeComponentInstanceIndex,
4399 ty: TypeFutureTableIndex,
4400 options: OptionsIndex,
4401 future: u32,
4402 address: u32,
4403 ) -> Result<u32> {
4404 instance
4405 .guest_write(
4406 StoreContextMut(self),
4407 caller,
4408 TransmitIndex::Future(ty),
4409 options,
4410 None,
4411 future,
4412 address,
4413 1,
4414 )
4415 .map(|result| result.encode())
4416 }
4417
4418 fn future_read(
4419 &mut self,
4420 instance: Instance,
4421 caller: RuntimeComponentInstanceIndex,
4422 ty: TypeFutureTableIndex,
4423 options: OptionsIndex,
4424 future: u32,
4425 address: u32,
4426 ) -> Result<u32> {
4427 instance
4428 .guest_read(
4429 StoreContextMut(self),
4430 caller,
4431 TransmitIndex::Future(ty),
4432 options,
4433 None,
4434 future,
4435 address,
4436 1,
4437 )
4438 .map(|result| result.encode())
4439 }
4440
4441 fn stream_write(
4442 &mut self,
4443 instance: Instance,
4444 caller: RuntimeComponentInstanceIndex,
4445 ty: TypeStreamTableIndex,
4446 options: OptionsIndex,
4447 stream: u32,
4448 address: u32,
4449 count: u32,
4450 ) -> Result<u32> {
4451 instance
4452 .guest_write(
4453 StoreContextMut(self),
4454 caller,
4455 TransmitIndex::Stream(ty),
4456 options,
4457 None,
4458 stream,
4459 address,
4460 count,
4461 )
4462 .map(|result| result.encode())
4463 }
4464
4465 fn stream_read(
4466 &mut self,
4467 instance: Instance,
4468 caller: RuntimeComponentInstanceIndex,
4469 ty: TypeStreamTableIndex,
4470 options: OptionsIndex,
4471 stream: u32,
4472 address: u32,
4473 count: u32,
4474 ) -> Result<u32> {
4475 instance
4476 .guest_read(
4477 StoreContextMut(self),
4478 caller,
4479 TransmitIndex::Stream(ty),
4480 options,
4481 None,
4482 stream,
4483 address,
4484 count,
4485 )
4486 .map(|result| result.encode())
4487 }
4488
4489 fn future_drop_writable(
4490 &mut self,
4491 instance: Instance,
4492 ty: TypeFutureTableIndex,
4493 writer: u32,
4494 ) -> Result<()> {
4495 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4496 }
4497
4498 fn flat_stream_write(
4499 &mut self,
4500 instance: Instance,
4501 caller: RuntimeComponentInstanceIndex,
4502 ty: TypeStreamTableIndex,
4503 options: OptionsIndex,
4504 payload_size: u32,
4505 payload_align: u32,
4506 stream: u32,
4507 address: u32,
4508 count: u32,
4509 ) -> Result<u32> {
4510 instance
4511 .guest_write(
4512 StoreContextMut(self),
4513 caller,
4514 TransmitIndex::Stream(ty),
4515 options,
4516 Some(FlatAbi {
4517 size: payload_size,
4518 align: payload_align,
4519 }),
4520 stream,
4521 address,
4522 count,
4523 )
4524 .map(|result| result.encode())
4525 }
4526
4527 fn flat_stream_read(
4528 &mut self,
4529 instance: Instance,
4530 caller: RuntimeComponentInstanceIndex,
4531 ty: TypeStreamTableIndex,
4532 options: OptionsIndex,
4533 payload_size: u32,
4534 payload_align: u32,
4535 stream: u32,
4536 address: u32,
4537 count: u32,
4538 ) -> Result<u32> {
4539 instance
4540 .guest_read(
4541 StoreContextMut(self),
4542 caller,
4543 TransmitIndex::Stream(ty),
4544 options,
4545 Some(FlatAbi {
4546 size: payload_size,
4547 align: payload_align,
4548 }),
4549 stream,
4550 address,
4551 count,
4552 )
4553 .map(|result| result.encode())
4554 }
4555
4556 fn stream_drop_writable(
4557 &mut self,
4558 instance: Instance,
4559 ty: TypeStreamTableIndex,
4560 writer: u32,
4561 ) -> Result<()> {
4562 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4563 }
4564
4565 fn error_context_debug_message(
4566 &mut self,
4567 instance: Instance,
4568 ty: TypeComponentLocalErrorContextTableIndex,
4569 options: OptionsIndex,
4570 err_ctx_handle: u32,
4571 debug_msg_address: u32,
4572 ) -> Result<()> {
4573 instance.error_context_debug_message(
4574 StoreContextMut(self),
4575 ty,
4576 options,
4577 err_ctx_handle,
4578 debug_msg_address,
4579 )
4580 }
4581
4582 fn thread_new_indirect(
4583 &mut self,
4584 instance: Instance,
4585 caller: RuntimeComponentInstanceIndex,
4586 func_ty_idx: TypeFuncIndex,
4587 start_func_table_idx: RuntimeTableIndex,
4588 start_func_idx: u32,
4589 context: i32,
4590 ) -> Result<u32> {
4591 instance.thread_new_indirect(
4592 StoreContextMut(self),
4593 caller,
4594 func_ty_idx,
4595 start_func_table_idx,
4596 start_func_idx,
4597 context,
4598 )
4599 }
4600}
4601
4602type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4603
4604pub(crate) struct HostTask {
4608 common: WaitableCommon,
4609
4610 caller: QualifiedThreadId,
4612
4613 call_context: CallContext,
4616
4617 state: HostTaskState,
4618}
4619
4620enum HostTaskState {
4621 CalleeStarted,
4626
4627 CalleeRunning(JoinHandle),
4632
4633 CalleeFinished(LiftedResult),
4637
4638 CalleeDone { cancelled: bool },
4641}
4642
4643impl HostTask {
4644 fn new(caller: QualifiedThreadId, state: HostTaskState) -> Self {
4645 Self {
4646 common: WaitableCommon::default(),
4647 call_context: CallContext::default(),
4648 caller,
4649 state,
4650 }
4651 }
4652}
4653
4654impl TableDebug for HostTask {
4655 fn type_name() -> &'static str {
4656 "HostTask"
4657 }
4658}
4659
4660type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4661
4662enum Caller {
4664 Host {
4666 tx: Option<oneshot::Sender<LiftedResult>>,
4668 host_future_present: bool,
4671 caller: CurrentThread,
4675 },
4676 Guest {
4678 thread: QualifiedThreadId,
4680 },
4681}
4682
4683struct LiftResult {
4686 lift: RawLift,
4687 ty: TypeTupleIndex,
4688 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4689 string_encoding: StringEncoding,
4690}
4691
4692#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4697pub(crate) struct QualifiedThreadId {
4698 task: TableId<GuestTask>,
4699 thread: TableId<GuestThread>,
4700}
4701
4702impl QualifiedThreadId {
4703 fn qualify(
4704 state: &mut ConcurrentState,
4705 thread: TableId<GuestThread>,
4706 ) -> Result<QualifiedThreadId> {
4707 Ok(QualifiedThreadId {
4708 task: state.get_mut(thread)?.parent_task,
4709 thread,
4710 })
4711 }
4712}
4713
4714impl fmt::Debug for QualifiedThreadId {
4715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4716 f.debug_tuple("QualifiedThreadId")
4717 .field(&self.task.rep())
4718 .field(&self.thread.rep())
4719 .finish()
4720 }
4721}
4722
4723enum GuestThreadState {
4724 NotStartedImplicit,
4725 NotStartedExplicit(
4726 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4727 ),
4728 Running,
4729 Suspended(StoreFiber<'static>),
4730 Ready {
4731 fiber: StoreFiber<'static>,
4732 cancellable: bool,
4733 },
4734 Completed,
4735}
4736pub struct GuestThread {
4737 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
4740 parent_task: TableId<GuestTask>,
4742 wake_on_cancel: Option<TableId<WaitableSet>>,
4745 state: GuestThreadState,
4747 instance_rep: Option<u32>,
4750 sync_call_set: TableId<WaitableSet>,
4752}
4753
4754impl GuestThread {
4755 fn from_instance(
4758 state: Pin<&mut ComponentInstance>,
4759 caller_instance: RuntimeComponentInstanceIndex,
4760 guest_thread: u32,
4761 ) -> Result<TableId<Self>> {
4762 let rep = state.instance_states().0[caller_instance]
4763 .thread_handle_table()
4764 .guest_thread_rep(guest_thread)?;
4765 Ok(TableId::new(rep))
4766 }
4767
4768 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
4769 let sync_call_set = state.push(WaitableSet {
4770 is_sync_call_set: true,
4771 ..WaitableSet::default()
4772 })?;
4773 Ok(Self {
4774 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
4775 parent_task,
4776 wake_on_cancel: None,
4777 state: GuestThreadState::NotStartedImplicit,
4778 instance_rep: None,
4779 sync_call_set,
4780 })
4781 }
4782
4783 fn new_explicit(
4784 state: &mut ConcurrentState,
4785 parent_task: TableId<GuestTask>,
4786 start_func: Box<
4787 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
4788 >,
4789 ) -> Result<Self> {
4790 let sync_call_set = state.push(WaitableSet {
4791 is_sync_call_set: true,
4792 ..WaitableSet::default()
4793 })?;
4794 Ok(Self {
4795 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
4796 parent_task,
4797 wake_on_cancel: None,
4798 state: GuestThreadState::NotStartedExplicit(start_func),
4799 instance_rep: None,
4800 sync_call_set,
4801 })
4802 }
4803}
4804
4805impl TableDebug for GuestThread {
4806 fn type_name() -> &'static str {
4807 "GuestThread"
4808 }
4809}
4810
4811enum SyncResult {
4812 NotProduced,
4813 Produced(Option<ValRaw>),
4814 Taken,
4815}
4816
4817impl SyncResult {
4818 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
4819 Ok(match mem::replace(self, SyncResult::Taken) {
4820 SyncResult::NotProduced => None,
4821 SyncResult::Produced(val) => Some(val),
4822 SyncResult::Taken => {
4823 bail_bug!("attempted to take a synchronous result that was already taken")
4824 }
4825 })
4826 }
4827}
4828
4829#[derive(Debug)]
4830enum HostFutureState {
4831 NotApplicable,
4832 Live,
4833 Dropped,
4834}
4835
4836pub(crate) struct GuestTask {
4838 common: WaitableCommon,
4840 lower_params: Option<RawLower>,
4842 lift_result: Option<LiftResult>,
4844 result: Option<LiftedResult>,
4847 callback: Option<CallbackFn>,
4850 caller: Caller,
4852 call_context: CallContext,
4857 sync_result: SyncResult,
4860 cancel_sent: bool,
4863 starting_sent: bool,
4866 instance: RuntimeInstance,
4873 event: Option<Event>,
4876 exited: bool,
4878 threads: HashSet<TableId<GuestThread>>,
4880 host_future_state: HostFutureState,
4883 async_function: bool,
4886
4887 decremented_interesting_task_count: bool,
4888}
4889
4890impl GuestTask {
4891 fn already_lowered_parameters(&self) -> bool {
4892 self.lower_params.is_none()
4894 }
4895
4896 fn returned_or_cancelled(&self) -> bool {
4897 self.lift_result.is_none()
4899 }
4900
4901 fn ready_to_delete(&self) -> bool {
4902 let threads_completed = self.threads.is_empty();
4903 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
4904 let pending_completion_event = matches!(
4905 self.common.event,
4906 Some(Event::Subtask {
4907 status: Status::Returned | Status::ReturnCancelled
4908 })
4909 );
4910 let ready = threads_completed
4911 && !has_sync_result
4912 && !pending_completion_event
4913 && !matches!(self.host_future_state, HostFutureState::Live);
4914 log::trace!(
4915 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
4916 threads_completed,
4917 has_sync_result,
4918 pending_completion_event,
4919 self.host_future_state
4920 );
4921 ready
4922 }
4923
4924 fn new(
4925 state: &mut ConcurrentState,
4926 lower_params: RawLower,
4927 lift_result: LiftResult,
4928 caller: Caller,
4929 callback: Option<CallbackFn>,
4930 instance: RuntimeInstance,
4931 async_function: bool,
4932 ) -> Result<QualifiedThreadId> {
4933 let host_future_state = match &caller {
4934 Caller::Guest { .. } => HostFutureState::NotApplicable,
4935 Caller::Host {
4936 host_future_present,
4937 ..
4938 } => {
4939 if *host_future_present {
4940 HostFutureState::Live
4941 } else {
4942 HostFutureState::NotApplicable
4943 }
4944 }
4945 };
4946 let task = state.push(Self {
4947 common: WaitableCommon::default(),
4948 lower_params: Some(lower_params),
4949 lift_result: Some(lift_result),
4950 result: None,
4951 callback,
4952 caller,
4953 call_context: CallContext::default(),
4954 sync_result: SyncResult::NotProduced,
4955 cancel_sent: false,
4956 starting_sent: false,
4957 instance,
4958 event: None,
4959 exited: false,
4960 threads: HashSet::new(),
4961 host_future_state,
4962 async_function,
4963 decremented_interesting_task_count: false,
4964 })?;
4965 let new_thread = GuestThread::new_implicit(state, task)?;
4966 let thread = state.push(new_thread)?;
4967 state.get_mut(task)?.threads.insert(thread);
4968 state.interesting_tasks += 1;
4969 Ok(QualifiedThreadId { task, thread })
4970 }
4971}
4972
4973impl TableDebug for GuestTask {
4974 fn type_name() -> &'static str {
4975 "GuestTask"
4976 }
4977}
4978
4979#[derive(Default)]
4981struct WaitableCommon {
4982 event: Option<Event>,
4984 set: Option<TableId<WaitableSet>>,
4986 handle: Option<u32>,
4988}
4989
4990#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4992enum Waitable {
4993 Host(TableId<HostTask>),
4995 Guest(TableId<GuestTask>),
4997 Transmit(TableId<TransmitHandle>),
4999}
5000
5001impl Waitable {
5002 fn from_instance(
5005 state: Pin<&mut ComponentInstance>,
5006 caller_instance: RuntimeComponentInstanceIndex,
5007 waitable: u32,
5008 ) -> Result<Self> {
5009 use crate::runtime::vm::component::Waitable;
5010
5011 let (waitable, kind) = state.instance_states().0[caller_instance]
5012 .handle_table()
5013 .waitable_rep(waitable)?;
5014
5015 Ok(match kind {
5016 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5017 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5018 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5019 })
5020 }
5021
5022 fn rep(&self) -> u32 {
5024 match self {
5025 Self::Host(id) => id.rep(),
5026 Self::Guest(id) => id.rep(),
5027 Self::Transmit(id) => id.rep(),
5028 }
5029 }
5030
5031 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5035 log::trace!("waitable {self:?} join set {set:?}");
5036
5037 let old = mem::replace(&mut self.common(state)?.set, set);
5038
5039 if let Some(old) = old {
5040 match *self {
5041 Waitable::Host(id) => state.remove_child(id, old),
5042 Waitable::Guest(id) => state.remove_child(id, old),
5043 Waitable::Transmit(id) => state.remove_child(id, old),
5044 }?;
5045
5046 state.get_mut(old)?.ready.remove(self);
5047 }
5048
5049 if let Some(set) = set {
5050 match *self {
5051 Waitable::Host(id) => state.add_child(id, set),
5052 Waitable::Guest(id) => state.add_child(id, set),
5053 Waitable::Transmit(id) => state.add_child(id, set),
5054 }?;
5055
5056 if self.common(state)?.event.is_some() {
5057 self.mark_ready(state)?;
5058 }
5059 }
5060
5061 Ok(())
5062 }
5063
5064 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5066 Ok(match self {
5067 Self::Host(id) => &mut state.get_mut(*id)?.common,
5068 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5069 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5070 })
5071 }
5072
5073 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5079 if self.common(state)?.set.is_some() {
5080 bail!(Trap::WaitableSyncAndAsync);
5081 }
5082 Ok(())
5083 }
5084
5085 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5089 log::trace!("set event for {self:?}: {event:?}");
5090 self.common(state)?.event = event;
5091 self.mark_ready(state)
5092 }
5093
5094 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5096 let common = self.common(state)?;
5097 let event = common.event.take();
5098 if let Some(set) = self.common(state)?.set {
5099 state.get_mut(set)?.ready.remove(self);
5100 }
5101
5102 Ok(event)
5103 }
5104
5105 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5109 if let Some(set) = self.common(state)?.set {
5110 state.get_mut(set)?.ready.insert(*self);
5111 if let Some((thread, mode)) = state.get_mut(set)?.waiting.pop_first() {
5112 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5113 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5114
5115 let item = match mode {
5116 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber),
5117 WaitMode::Callback(instance) => WorkItem::GuestCall(
5118 state.get_mut(thread.task)?.instance.index,
5119 GuestCall {
5120 thread,
5121 kind: GuestCallKind::DeliverEvent {
5122 instance,
5123 set: Some(set),
5124 },
5125 },
5126 ),
5127 };
5128 state.push_high_priority(item);
5129 }
5130 }
5131 Ok(())
5132 }
5133
5134 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5136 match self {
5137 Self::Host(task) => {
5138 log::trace!("delete host task {task:?}");
5139 state.delete(*task)?;
5140 }
5141 Self::Guest(task) => {
5142 log::trace!("delete guest task {task:?}");
5143 let task = state.delete(*task)?;
5144
5145 debug_assert!(task.decremented_interesting_task_count);
5152 }
5153 Self::Transmit(task) => {
5154 state.delete(*task)?;
5155 }
5156 }
5157
5158 Ok(())
5159 }
5160}
5161
5162impl fmt::Debug for Waitable {
5163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5164 match self {
5165 Self::Host(id) => write!(f, "{id:?}"),
5166 Self::Guest(id) => write!(f, "{id:?}"),
5167 Self::Transmit(id) => write!(f, "{id:?}"),
5168 }
5169 }
5170}
5171
5172#[derive(Default)]
5174struct WaitableSet {
5175 ready: BTreeSet<Waitable>,
5177 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5179 is_sync_call_set: bool,
5182}
5183
5184impl TableDebug for WaitableSet {
5185 fn type_name() -> &'static str {
5186 "WaitableSet"
5187 }
5188}
5189
5190type RawLower =
5192 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5193
5194type RawLift = Box<
5196 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5197>;
5198
5199type LiftedResult = Box<dyn Any + Send + Sync>;
5203
5204struct DummyResult;
5207
5208#[derive(Default)]
5210pub struct ConcurrentInstanceState {
5211 backpressure: u16,
5213 do_not_enter: bool,
5215 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5218}
5219
5220impl ConcurrentInstanceState {
5221 pub fn pending_is_empty(&self) -> bool {
5222 self.pending.is_empty()
5223 }
5224}
5225
5226#[derive(Debug, Copy, Clone)]
5227pub(crate) enum CurrentThread {
5228 Guest(QualifiedThreadId),
5229 Host(TableId<HostTask>),
5230 None,
5231}
5232
5233impl CurrentThread {
5234 fn guest(&self) -> Option<&QualifiedThreadId> {
5235 match self {
5236 Self::Guest(id) => Some(id),
5237 _ => None,
5238 }
5239 }
5240
5241 fn host(&self) -> Option<TableId<HostTask>> {
5242 match self {
5243 Self::Host(id) => Some(*id),
5244 _ => None,
5245 }
5246 }
5247
5248 fn is_none(&self) -> bool {
5249 matches!(self, Self::None)
5250 }
5251}
5252
5253impl From<QualifiedThreadId> for CurrentThread {
5254 fn from(id: QualifiedThreadId) -> Self {
5255 Self::Guest(id)
5256 }
5257}
5258
5259impl From<TableId<HostTask>> for CurrentThread {
5260 fn from(id: TableId<HostTask>) -> Self {
5261 Self::Host(id)
5262 }
5263}
5264
5265pub struct ConcurrentState {
5267 unforced_current_thread: CurrentThread,
5273
5274 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5279 table: AlwaysMut<ResourceTable>,
5281 high_priority: Vec<WorkItem>,
5283 low_priority: VecDeque<WorkItem>,
5285 suspend_reason: Option<SuspendReason>,
5289 worker: Option<StoreFiber<'static>>,
5293 worker_item: Option<WorkerItem>,
5295
5296 global_error_context_ref_counts:
5309 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5310
5311 interesting_tasks: usize,
5324
5325 interesting_tasks_empty_waker: Option<Waker>,
5329
5330 ready_for_concurrent_call_waker: Option<Waker>,
5335}
5336
5337impl Default for ConcurrentState {
5338 fn default() -> Self {
5339 Self {
5340 unforced_current_thread: CurrentThread::None,
5341 table: AlwaysMut::new(ResourceTable::new()),
5342 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5343 high_priority: Vec::new(),
5344 low_priority: VecDeque::new(),
5345 suspend_reason: None,
5346 worker: None,
5347 worker_item: None,
5348 global_error_context_ref_counts: BTreeMap::new(),
5349 interesting_tasks: 0,
5350 interesting_tasks_empty_waker: None,
5351 ready_for_concurrent_call_waker: None,
5352 }
5353 }
5354}
5355
5356impl ConcurrentState {
5357 pub(crate) fn take_fibers_and_futures(
5374 &mut self,
5375 fibers: &mut Vec<StoreFiber<'static>>,
5376 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5377 ) {
5378 for entry in self.table.get_mut().iter_mut() {
5379 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5380 for mode in mem::take(&mut set.waiting).into_values() {
5381 if let WaitMode::Fiber(fiber) = mode {
5382 fibers.push(fiber);
5383 }
5384 }
5385 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5386 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5387 mem::replace(&mut thread.state, GuestThreadState::Completed)
5388 {
5389 fibers.push(fiber);
5390 }
5391 }
5392 }
5393
5394 if let Some(fiber) = self.worker.take() {
5395 fibers.push(fiber);
5396 }
5397
5398 let mut handle_item = |item| match item {
5399 WorkItem::ResumeFiber(fiber) => {
5400 fibers.push(fiber);
5401 }
5402 WorkItem::PushFuture(future) => {
5403 self.futures
5404 .get_mut()
5405 .as_mut()
5406 .unwrap()
5407 .push(future.into_inner());
5408 }
5409 WorkItem::ResumeThread(..) | WorkItem::GuestCall(..) | WorkItem::WorkerFunction(..) => {
5410 }
5411 };
5412
5413 for item in mem::take(&mut self.high_priority) {
5414 handle_item(item);
5415 }
5416 for item in mem::take(&mut self.low_priority) {
5417 handle_item(item);
5418 }
5419
5420 if let Some(them) = self.futures.get_mut().take() {
5421 futures.push(them);
5422 }
5423 }
5424
5425 #[cfg(feature = "gc")]
5426 pub(crate) fn trace_fiber_roots(
5427 &mut self,
5428 modules: &ModuleRegistry,
5429 unwind: &dyn Unwind,
5430 gc_roots_list: &mut GcRootsList,
5431 ) {
5432 let ConcurrentState {
5433 table,
5434 worker,
5435 high_priority,
5436 low_priority,
5437
5438 futures: _,
5442
5443 worker_item: _,
5445 unforced_current_thread: _,
5446 suspend_reason: _,
5447 global_error_context_ref_counts: _,
5448 interesting_tasks: _,
5449 interesting_tasks_empty_waker: _,
5450 ready_for_concurrent_call_waker: _,
5451 } = self;
5452
5453 for entry in table.get_mut().iter_mut() {
5454 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5455 for mode in set.waiting.values_mut() {
5456 if let WaitMode::Fiber(fiber) = mode {
5457 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5458 }
5459 }
5460 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5461 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5462 &mut thread.state
5463 {
5464 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5465 }
5466 }
5467 }
5468
5469 if let Some(fiber) = worker {
5470 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5471 }
5472
5473 let mut handle_item = |item: &mut WorkItem| match item {
5474 WorkItem::ResumeFiber(fiber) => {
5475 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5476 }
5477 WorkItem::PushFuture(_future) => {
5478 }
5481 WorkItem::ResumeThread(..) | WorkItem::GuestCall(..) | WorkItem::WorkerFunction(..) => {
5482 }
5483 };
5484
5485 for item in high_priority {
5486 handle_item(item);
5487 }
5488 for item in low_priority {
5489 handle_item(item);
5490 }
5491 }
5492
5493 fn push<V: Send + Sync + 'static>(
5494 &mut self,
5495 value: V,
5496 ) -> Result<TableId<V>, ResourceTableError> {
5497 self.table.get_mut().push(value).map(TableId::from)
5498 }
5499
5500 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5501 self.table.get_mut().get_mut(&Resource::from(id))
5502 }
5503
5504 pub fn add_child<T: 'static, U: 'static>(
5505 &mut self,
5506 child: TableId<T>,
5507 parent: TableId<U>,
5508 ) -> Result<(), ResourceTableError> {
5509 self.table
5510 .get_mut()
5511 .add_child(Resource::from(child), Resource::from(parent))
5512 }
5513
5514 pub fn remove_child<T: 'static, U: 'static>(
5515 &mut self,
5516 child: TableId<T>,
5517 parent: TableId<U>,
5518 ) -> Result<(), ResourceTableError> {
5519 self.table
5520 .get_mut()
5521 .remove_child(Resource::from(child), Resource::from(parent))
5522 }
5523
5524 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5525 self.table.get_mut().delete(Resource::from(id))
5526 }
5527
5528 fn push_future(&mut self, future: HostTaskFuture) {
5529 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5536 }
5537
5538 fn push_high_priority(&mut self, item: WorkItem) {
5539 log::trace!("push high priority: {item:?}");
5540 self.high_priority.push(item);
5541 }
5542
5543 fn push_low_priority(&mut self, item: WorkItem) {
5544 log::trace!("push low priority: {item:?}");
5545 self.low_priority.push_front(item);
5546 }
5547
5548 fn push_work_item(&mut self, item: WorkItem, high_priority: bool) {
5549 if high_priority {
5550 self.push_high_priority(item);
5551 } else {
5552 self.push_low_priority(item);
5553 }
5554 }
5555
5556 fn promote_instance_local_thread_work_item(
5557 &mut self,
5558 current_instance: RuntimeComponentInstanceIndex,
5559 ) -> bool {
5560 self.promote_work_items_matching(|item: &WorkItem| match item {
5561 WorkItem::ResumeThread(instance, _) | WorkItem::GuestCall(instance, _) => {
5562 *instance == current_instance
5563 }
5564 _ => false,
5565 })
5566 }
5567
5568 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> bool {
5569 self.promote_work_items_matching(|item: &WorkItem| match item {
5570 WorkItem::ResumeThread(_, t) | WorkItem::GuestCall(_, GuestCall { thread: t, .. }) => {
5571 *t == thread
5572 }
5573 _ => false,
5574 })
5575 }
5576
5577 fn promote_work_items_matching<F>(&mut self, mut predicate: F) -> bool
5578 where
5579 F: FnMut(&WorkItem) -> bool,
5580 {
5581 if self.high_priority.iter().any(&mut predicate) {
5585 true
5586 }
5587 else if let Some(idx) = self.low_priority.iter().position(&mut predicate) {
5590 let item = self.low_priority.remove(idx).unwrap();
5591 self.push_high_priority(item);
5592 true
5593 } else {
5594 false
5595 }
5596 }
5597
5598 fn check_blocking_for(&mut self, task: TableId<GuestTask>) -> Result<()> {
5599 if self.may_block(task)? {
5600 Ok(())
5601 } else {
5602 Err(Trap::CannotBlockSyncTask.into())
5603 }
5604 }
5605
5606 fn may_block(&mut self, task: TableId<GuestTask>) -> Result<bool> {
5607 let task = self.get_mut(task)?;
5608 Ok(task.async_function || task.returned_or_cancelled())
5609 }
5610
5611 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
5617 let (task, is_host) = (task >> 1, task & 1 == 1);
5618 if is_host {
5619 let task: TableId<HostTask> = TableId::new(task);
5620 Ok(&mut self.get_mut(task)?.call_context)
5621 } else {
5622 let task: TableId<GuestTask> = TableId::new(task);
5623 Ok(&mut self.get_mut(task)?.call_context)
5624 }
5625 }
5626
5627 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
5628 match self.futures.get_mut().as_mut() {
5629 Some(f) => Ok(f),
5630 None => bail_bug!("futures field of concurrent state is currently taken"),
5631 }
5632 }
5633
5634 pub(crate) fn table(&mut self) -> &mut ResourceTable {
5635 self.table.get_mut()
5636 }
5637
5638 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
5640 match cur {
5641 CurrentThread::Guest(thread) => {
5642 let task = self.get_mut(thread.task).ok()?;
5643 Some(match task.caller {
5644 Caller::Host { caller, .. } => caller,
5645 Caller::Guest { thread } => thread.into(),
5646 })
5647 }
5648 CurrentThread::Host(id) => Some(self.get_mut(id).ok()?.caller.into()),
5649 CurrentThread::None => None,
5650 }
5651 }
5652}
5653
5654fn for_any_lower<
5657 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
5658>(
5659 fun: F,
5660) -> F {
5661 fun
5662}
5663
5664fn for_any_lift<
5666 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5667>(
5668 fun: F,
5669) -> F {
5670 fun
5671}
5672
5673fn check_ambient_store(id: StoreId) {
5674 let message = "\
5675 `Future`s which depend on asynchronous component tasks, streams, or \
5676 futures to complete may only be polled from the event loop of the \
5677 store to which they belong. Please use \
5678 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
5679 ";
5680 tls::try_get(|store| {
5681 let matched = match store {
5682 tls::TryGet::Some(store) => store.id() == id,
5683 tls::TryGet::Taken | tls::TryGet::None => false,
5684 };
5685
5686 if !matched {
5687 panic!("{message}")
5688 }
5689 });
5690}
5691
5692fn check_recursive_run() {
5695 tls::try_get(|store| {
5696 if !matches!(store, tls::TryGet::None) {
5697 panic!("Recursive `StoreContextMut::run_concurrent` calls not supported")
5698 }
5699 });
5700}
5701
5702fn unpack_callback_code(code: u32) -> (u32, u32) {
5703 (code & 0xF, code >> 4)
5704}
5705
5706struct WaitableCheckParams {
5710 set: TableId<WaitableSet>,
5711 options: OptionsIndex,
5712 payload: u32,
5713}
5714
5715enum WaitableCheck {
5718 Wait,
5719 Poll,
5720}
5721
5722#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
5731pub struct GuestTaskId(TableId<GuestTask>);
5732
5733pub(crate) struct PreparedCall<R> {
5735 handle: Func,
5737 thread: QualifiedThreadId,
5739 param_count: usize,
5741 rx: oneshot::Receiver<LiftedResult>,
5744 runtime_instance: RuntimeInstance,
5746 _phantom: PhantomData<R>,
5747}
5748
5749impl<R> PreparedCall<R> {
5750 pub(crate) fn task_id(&self) -> TaskId {
5752 TaskId {
5753 task: self.thread.task,
5754 runtime_instance: self.runtime_instance,
5755 }
5756 }
5757}
5758
5759pub(crate) struct TaskId {
5761 task: TableId<GuestTask>,
5762 runtime_instance: RuntimeInstance,
5763}
5764
5765impl TaskId {
5766 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
5772 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
5773 let delete = if !task.already_lowered_parameters() {
5774 store.cancel_guest_subtask_without_lowered_parameters(
5775 self.runtime_instance,
5776 self.task,
5777 )?;
5778 true
5779 } else {
5780 task.host_future_state = HostFutureState::Dropped;
5781 task.ready_to_delete()
5782 };
5783 if delete {
5784 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
5785 }
5786 Ok(())
5787 }
5788}
5789
5790pub(crate) fn prepare_call<T, R>(
5796 mut store: StoreContextMut<T>,
5797 handle: Func,
5798 param_count: usize,
5799 host_future_present: bool,
5800 lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
5801 + Send
5802 + Sync
5803 + 'static,
5804 lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
5805 + Send
5806 + Sync
5807 + 'static,
5808) -> Result<PreparedCall<R>> {
5809 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
5810
5811 let instance = handle.instance().id().get(store.0);
5812 let options = &instance.component().env_component().options[options];
5813 let ty = &instance.component().types()[ty];
5814 let async_function = ty.async_;
5815 let task_return_type = ty.results;
5816 let component_instance = raw_options.instance;
5817 let callback = options.callback.map(|i| instance.runtime_callback(i));
5818 let memory = options
5819 .memory()
5820 .map(|i| instance.runtime_memory(i))
5821 .map(SendSyncPtr::new);
5822 let string_encoding = options.string_encoding;
5823 let token = StoreToken::new(store.as_context_mut());
5824 let caller = store.0.current_thread()?;
5825 let state = store.0.concurrent_state_mut()?;
5826
5827 let (tx, rx) = oneshot::channel();
5828
5829 let instance = handle.instance().runtime_instance(component_instance);
5830 let thread = GuestTask::new(
5831 state,
5832 Box::new(for_any_lower(move |store, params| {
5833 lower_params(handle, token.as_context_mut(store), params)
5834 })),
5835 LiftResult {
5836 lift: Box::new(for_any_lift(move |store, result| {
5837 lift_result(handle, store, result)
5838 })),
5839 ty: task_return_type,
5840 memory,
5841 string_encoding,
5842 },
5843 Caller::Host {
5844 tx: Some(tx),
5845 host_future_present,
5846 caller,
5847 },
5848 callback.map(|callback| {
5849 let callback = SendSyncPtr::new(callback);
5850 let instance = handle.instance();
5851 Box::new(move |store: &mut dyn VMStore, event, handle| {
5852 let store = token.as_context_mut(store);
5853 unsafe { instance.call_callback(store, callback, event, handle) }
5856 }) as CallbackFn
5857 }),
5858 instance,
5859 async_function,
5860 )?;
5861
5862 if !store.0.may_enter(instance)? {
5863 bail!(Trap::CannotEnterComponent);
5864 }
5865
5866 Ok(PreparedCall {
5867 handle,
5868 thread,
5869 param_count,
5870 runtime_instance: instance,
5871 rx,
5872 _phantom: PhantomData,
5873 })
5874}
5875
5876pub(crate) struct QueuedCall<R> {
5877 store: StoreId,
5878 task: TableId<GuestTask>,
5879 rx: oneshot::Receiver<LiftedResult>,
5880 _marker: PhantomData<fn() -> R>,
5881}
5882
5883impl<R> QueuedCall<R> {
5884 pub(crate) fn new<T: 'static>(
5891 mut store: StoreContextMut<T>,
5892 prepared: PreparedCall<R>,
5893 ) -> Result<QueuedCall<R>> {
5894 let PreparedCall {
5895 handle,
5896 thread,
5897 param_count,
5898 rx,
5899 ..
5900 } = prepared;
5901
5902 queue_call0(store.as_context_mut(), handle, thread, param_count)?;
5903
5904 Ok(QueuedCall {
5905 store: store.0.id(),
5906 task: thread.task,
5907 rx,
5908 _marker: PhantomData,
5909 })
5910 }
5911
5912 fn task(&self) -> GuestTaskId {
5913 GuestTaskId(self.task)
5914 }
5915}
5916
5917impl<R> Future for QueuedCall<R>
5918where
5919 R: 'static,
5920{
5921 type Output = Result<R>;
5922
5923 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
5924 check_ambient_store(self.store);
5925 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
5926 Ok(r) => match r.downcast() {
5927 Ok(r) => Ok(*r),
5928 Err(_) => bail_bug!("wrong type of value produced"),
5929 },
5930 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
5931 })
5932 }
5933}
5934
5935fn queue_call0<T: 'static>(
5938 store: StoreContextMut<T>,
5939 handle: Func,
5940 guest_thread: QualifiedThreadId,
5941 param_count: usize,
5942) -> Result<()> {
5943 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
5944 let is_concurrent = raw_options.async_;
5945 let callback = raw_options.callback;
5946 let instance = handle.instance();
5947 let callee = handle.lifted_core_func(store.0);
5948 let post_return = handle.post_return_core_func(store.0);
5949 let callback = callback.map(|i| {
5950 let instance = instance.id().get(store.0);
5951 SendSyncPtr::new(instance.runtime_callback(i))
5952 });
5953
5954 log::trace!("queueing call {guest_thread:?}");
5955
5956 unsafe {
5960 instance.queue_call(
5961 store,
5962 guest_thread,
5963 SendSyncPtr::new(callee),
5964 param_count,
5965 1,
5966 is_concurrent,
5967 callback,
5968 post_return.map(SendSyncPtr::new),
5969 )
5970 }
5971}