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 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1605 if !self.concurrency_support() {
1607 return Ok(CurrentThread::None);
1608 }
1609
1610 if !self
1613 .vm_store_context_mut()
1614 .current_thread_mut()
1615 .is_deferred()
1616 {
1617 return Ok(self
1618 .concurrent_state_mut_already_forced_current_thread()
1619 .unforced_current_thread);
1620 }
1621
1622 let state = self.concurrent_state_mut_without_forcing_current_thread();
1631 let id = match state.unforced_current_thread.guest().copied() {
1632 Some(thread) => state.get_mut(thread.task)?.instance.instance,
1633 None => bail_bug!("deferred component-model thread with non-guest base"),
1634 };
1635
1636 let mut frames = Vec::new();
1639 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1640 while let Some(ptr) = cur.as_deferred() {
1641 let deferred = unsafe { ptr.as_non_null().as_ref() };
1646 frames.push((
1647 deferred.callee_async != 0,
1648 deferred.callee_instance,
1649 deferred.saved_context,
1650 ));
1651 cur = deferred.parent;
1652 }
1653
1654 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1658
1659 let current_context = *self.vm_store_context_mut().component_context_mut();
1662
1663 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1667 *self.vm_store_context_mut().component_context_mut() = saved_context;
1671 let callee = RuntimeInstance {
1672 instance: id,
1673 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1674 };
1675 self.enter_guest_sync_call(None, callee_async, callee)?;
1676 }
1677
1678 *self.vm_store_context_mut().component_context_mut() = current_context;
1680
1681 Ok(self
1682 .concurrent_state_mut_without_forcing_current_thread()
1683 .unforced_current_thread)
1684 }
1685
1686 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1687 match self.current_thread()?.guest() {
1688 Some(id) => Ok(*id),
1689 None => bail_bug!("current thread is not a guest thread"),
1690 }
1691 }
1692
1693 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1694 match self.current_thread()?.host() {
1695 Some(id) => Ok(id),
1696 None => bail_bug!("current thread is not a host thread"),
1697 }
1698 }
1699
1700 fn take_pending_cancellation(&mut self) -> Result<bool> {
1703 let thread = self.current_guest_thread()?;
1704 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1705 if let Some(Event::Cancelled) = task.event {
1706 task.event.take();
1707 return Ok(true);
1708 }
1709 Ok(false)
1710 }
1711
1712 pub(crate) fn enter_guest_sync_call(
1724 &mut self,
1725 guest_caller: Option<RuntimeInstance>,
1726 callee_async: bool,
1727 callee: RuntimeInstance,
1728 ) -> Result<()> {
1729 log::trace!("enter sync call {callee:?}");
1730 if !self.concurrency_support() {
1731 return self.enter_call_not_concurrent();
1732 }
1733
1734 let thread = self.current_thread()?;
1735 let state = self.concurrent_state_mut()?;
1736 let instance = if let Some(thread) = thread.guest() {
1737 Some(state.get_mut(thread.task)?.instance)
1738 } else {
1739 None
1740 };
1741 if guest_caller.is_some() {
1742 debug_assert_eq!(instance, guest_caller);
1743 }
1744 let guest_thread = GuestTask::new(
1745 state,
1746 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1747 LiftResult {
1748 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1749 ty: TypeTupleIndex::reserved_value(),
1750 memory: None,
1751 string_encoding: StringEncoding::Utf8,
1752 },
1753 if let Some(thread) = thread.guest() {
1754 Caller::Guest { thread: *thread }
1755 } else {
1756 Caller::Host {
1757 tx: None,
1758 host_future_present: false,
1759 caller: thread,
1760 }
1761 },
1762 None,
1763 callee,
1764 callee_async,
1765 )?;
1766
1767 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1768 guest_thread.thread,
1769 self,
1770 callee.index,
1771 )?;
1772 self.set_thread(guest_thread)?;
1773
1774 Ok(())
1775 }
1776
1777 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1785 if !self.concurrency_support() {
1786 return Ok(self.exit_call_not_concurrent());
1787 }
1788 let thread = match self.set_thread(CurrentThread::None)?.guest() {
1789 Some(t) => *t,
1790 None => bail_bug!("expected task when exiting"),
1791 };
1792 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1793 let instance = task.instance;
1794 let caller = match &task.caller {
1795 &Caller::Guest { thread } => thread.into(),
1796 &Caller::Host { caller, .. } => caller,
1797 };
1798 task.lift_result = None;
1799 task.exited = true;
1800 self.set_thread(caller)?;
1801
1802 log::trace!("exit sync call {instance:?}");
1803 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1804
1805 Ok(())
1806 }
1807
1808 pub(crate) fn host_task_create(&mut self) -> Result<Option<TableId<HostTask>>> {
1816 if !self.concurrency_support() {
1817 self.enter_call_not_concurrent()?;
1818 return Ok(None);
1819 }
1820 let caller = self.current_guest_thread()?;
1821 let state = self.concurrent_state_mut()?;
1822 let task = state.push(HostTask::new(caller, HostTaskState::CalleeStarted))?;
1823 log::trace!("new host task {task:?}");
1824 self.set_thread(task)?;
1825 Ok(Some(task))
1826 }
1827
1828 pub fn host_task_reenter_caller(&mut self) -> Result<()> {
1834 if !self.concurrency_support() {
1835 return Ok(());
1836 }
1837 let task = self.current_host_thread()?;
1838 let caller = self.concurrent_state_mut()?.get_mut(task)?.caller;
1839 self.set_thread(caller)?;
1840 Ok(())
1841 }
1842
1843 pub(crate) fn host_task_delete(&mut self, task: Option<TableId<HostTask>>) -> Result<()> {
1850 match task {
1851 Some(task) => {
1852 log::trace!("delete host task {task:?}");
1853 self.concurrent_state_mut()?.delete(task)?;
1854 }
1855 None => {
1856 self.exit_call_not_concurrent();
1857 }
1858 }
1859 Ok(())
1860 }
1861
1862 pub(crate) fn may_enter(&mut self, instance: RuntimeInstance) -> Result<bool> {
1870 if self.trapped() {
1871 return Ok(false);
1872 }
1873 if !self.concurrency_support() {
1874 return Ok(true);
1875 }
1876 let mut cur = Some(self.current_thread()?);
1877 let state = self.concurrent_state_mut()?;
1878 while let Some(t) = cur {
1879 if let Some(thread) = t.guest() {
1880 let task = state.get_mut(thread.task)?;
1881 if task.instance.instance == instance.instance {
1888 return Ok(false);
1889 }
1890 }
1891 cur = state.parent(t);
1892 }
1893 Ok(true)
1894 }
1895
1896 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
1899 self.component_instance_mut(instance.instance)
1900 .instance_state(instance.index)
1901 }
1902
1903 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
1909 let thread = thread.into();
1910 let state = self.concurrent_state_mut()?;
1911 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
1912
1913 if let Some(old_thread) = old_thread.guest() {
1921 let old_context = *self.vm_store_context_mut().component_context_mut();
1922 self.concurrent_state_mut()?
1923 .get_mut(old_thread.thread)?
1924 .context = old_context;
1925 }
1926 if cfg!(debug_assertions) {
1927 *self.vm_store_context_mut().component_context_mut() =
1928 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
1929 }
1930 if let Some(thread) = thread.guest() {
1931 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1932 let context = thread.context;
1933 if cfg!(debug_assertions) {
1934 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
1935 }
1936 *self.vm_store_context_mut().component_context_mut() = context;
1937 }
1938
1939 let state = self.concurrent_state_mut()?;
1947 if let Some(old_thread) = old_thread.guest() {
1948 let instance = state.get_mut(old_thread.task)?.instance.instance;
1949 self.component_instance_mut(instance)
1950 .set_task_may_block(false)
1951 }
1952
1953 if thread.guest().is_some() {
1954 self.set_task_may_block()?;
1955 }
1956
1957 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
1959 VMLazyThread::none()
1960 } else {
1961 VMLazyThread::forced()
1962 };
1963
1964 Ok(old_thread)
1965 }
1966
1967 fn set_task_may_block(&mut self) -> Result<()> {
1970 let guest_thread = self.current_guest_thread()?;
1971 let state = self.concurrent_state_mut()?;
1972 let instance = state.get_mut(guest_thread.task)?.instance.instance;
1973 let may_block = self.concurrent_state_mut()?.may_block(guest_thread.task)?;
1974 self.component_instance_mut(instance)
1975 .set_task_may_block(may_block);
1976 Ok(())
1977 }
1978
1979 pub(crate) fn check_blocking(&mut self) -> Result<()> {
1980 if !self.concurrency_support() {
1981 return Ok(());
1982 }
1983 let task = self.current_guest_thread()?.task;
1984 let state = self.concurrent_state_mut()?;
1985 let instance = state.get_mut(task)?.instance.instance;
1986 let task_may_block = self.component_instance(instance).get_task_may_block();
1987
1988 if task_may_block {
1989 Ok(())
1990 } else {
1991 Err(Trap::CannotBlockSyncTask.into())
1992 }
1993 }
1994
1995 fn enter_instance(&mut self, instance: RuntimeInstance) {
1999 log::trace!("enter {instance:?}");
2000 self.instance_state(instance)
2001 .concurrent_state()
2002 .do_not_enter = true;
2003 }
2004
2005 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2009 log::trace!("exit {instance:?}");
2010 self.instance_state(instance)
2011 .concurrent_state()
2012 .do_not_enter = false;
2013 self.partition_pending(instance)
2014 }
2015
2016 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2024 for (thread, kind) in
2025 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2026 {
2027 let call = GuestCall { thread, kind };
2028 if call.is_ready(self)? {
2029 self.concurrent_state_mut()?
2030 .push_high_priority(WorkItem::GuestCall(instance.index, call));
2031 } else {
2032 self.instance_state(instance)
2033 .concurrent_state()
2034 .pending
2035 .insert(call.thread, call.kind);
2036 }
2037 }
2038
2039 if let Some(waker) = self
2040 .concurrent_state_mut()?
2041 .ready_for_concurrent_call_waker
2042 .take()
2043 {
2044 waker.wake();
2045 }
2046
2047 Ok(())
2048 }
2049
2050 pub(crate) fn backpressure_modify(
2052 &mut self,
2053 caller_instance: RuntimeInstance,
2054 modify: impl FnOnce(u16) -> Option<u16>,
2055 ) -> Result<()> {
2056 let state = self.instance_state(caller_instance).concurrent_state();
2057 let old = state.backpressure;
2058 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2059 state.backpressure = new;
2060
2061 if old > 0 && new == 0 {
2062 self.partition_pending(caller_instance)?;
2065 }
2066
2067 Ok(())
2068 }
2069
2070 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2073 let old_thread = self.current_thread()?;
2074 log::trace!("resume_fiber: save current thread {old_thread:?}");
2075
2076 let fiber = fiber::resolve_or_release(self, fiber).await?;
2077
2078 self.set_thread(old_thread)?;
2079
2080 let state = self.concurrent_state_mut()?;
2081
2082 if let Some(ot) = old_thread.guest() {
2083 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2084 }
2085 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2086
2087 if let Some(mut fiber) = fiber {
2088 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2089 let reason = match state.suspend_reason.take() {
2091 Some(r) => r,
2092 None => bail_bug!("suspend reason missing when resuming fiber"),
2093 };
2094 match reason {
2095 SuspendReason::NeedWork => {
2096 if state.worker.is_none() {
2097 state.worker = Some(fiber);
2098 } else {
2099 fiber.dispose(self);
2100 }
2101 }
2102 SuspendReason::Yielding {
2103 thread,
2104 cancellable,
2105 ..
2106 } => {
2107 state.get_mut(thread.thread)?.state =
2108 GuestThreadState::Ready { fiber, cancellable };
2109 let instance = state.get_mut(thread.task)?.instance.index;
2110 state.push_low_priority(WorkItem::ResumeThread(instance, thread));
2111 }
2112 SuspendReason::ExplicitlySuspending { thread, .. } => {
2113 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2114 }
2115 SuspendReason::Waiting { set, thread, .. } => {
2116 let old = state
2117 .get_mut(set)?
2118 .waiting
2119 .insert(thread, WaitMode::Fiber(fiber));
2120 assert!(old.is_none());
2121 }
2122 };
2123 } else {
2124 log::trace!("resume_fiber: fiber has exited");
2125 }
2126
2127 Ok(())
2128 }
2129
2130 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2136 log::trace!("suspend fiber: {reason:?}");
2137
2138 let task = match &reason {
2142 SuspendReason::Yielding { thread, .. }
2143 | SuspendReason::Waiting { thread, .. }
2144 | SuspendReason::ExplicitlySuspending { thread, .. } => Some(thread.task),
2145 SuspendReason::NeedWork => None,
2146 };
2147
2148 let old_guest_thread = if task.is_some() {
2149 self.current_thread()?
2150 } else {
2151 CurrentThread::None
2152 };
2153
2154 debug_assert!(
2160 matches!(
2161 reason,
2162 SuspendReason::ExplicitlySuspending {
2163 skip_may_block_check: true,
2164 ..
2165 } | SuspendReason::Waiting {
2166 skip_may_block_check: true,
2167 ..
2168 } | SuspendReason::Yielding {
2169 skip_may_block_check: true,
2170 ..
2171 }
2172 ) || old_guest_thread
2173 .guest()
2174 .map(|thread| self.concurrent_state_mut()?.may_block(thread.task))
2175 .transpose()?
2176 .unwrap_or(true)
2177 );
2178
2179 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2180 assert!(suspend_reason.is_none());
2181 *suspend_reason = Some(reason);
2182
2183 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2184
2185 if task.is_some() {
2186 self.set_thread(old_guest_thread)?;
2187 }
2188
2189 Ok(())
2190 }
2191
2192 fn wait_for_event(&mut self, waitable: Waitable) -> Result<()> {
2193 let caller = self.current_guest_thread()?;
2194 let state = self.concurrent_state_mut()?;
2195
2196 waitable.trap_if_in_waitable_set(state)?;
2197
2198 let set = state.get_mut(caller.thread)?.sync_call_set;
2199 waitable.join(state, Some(set))?;
2200 self.suspend(SuspendReason::Waiting {
2201 set,
2202 thread: caller,
2203 skip_may_block_check: false,
2204 })?;
2205 let state = self.concurrent_state_mut()?;
2206 waitable.join(state, None)
2207 }
2208
2209 fn cleanup_thread(
2231 &mut self,
2232 guest_thread: QualifiedThreadId,
2233 runtime_instance: RuntimeInstance,
2234 cleanup_task: CleanupTask,
2235 ) -> Result<()> {
2236 let state = self.concurrent_state_mut()?;
2237 let thread_data = state.get_mut(guest_thread.thread)?;
2238 let sync_call_set = thread_data.sync_call_set;
2239 if let Some(guest_id) = thread_data.instance_rep {
2240 self.instance_state(runtime_instance)
2241 .thread_handle_table()
2242 .guest_thread_remove(guest_id)?;
2243 }
2244 let state = self.concurrent_state_mut()?;
2245
2246 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2248 if let Some(Event::Subtask {
2249 status: Status::Returned | Status::ReturnCancelled,
2250 }) = waitable.common(state)?.event
2251 {
2252 waitable.delete_from(state)?;
2253 }
2254 }
2255
2256 state.delete(guest_thread.thread)?;
2257 state.delete(sync_call_set)?;
2258 let task = state.get_mut(guest_thread.task)?;
2259 task.threads.remove(&guest_thread.thread);
2260
2261 if task.threads.is_empty() && !task.returned_or_cancelled() {
2262 bail!(Trap::NoAsyncResult);
2263 }
2264 let ready_to_delete = task.ready_to_delete();
2265
2266 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2267 task.decremented_interesting_task_count = true;
2268
2269 debug_assert!(state.interesting_tasks > 0);
2270 state.interesting_tasks -= 1;
2271 if state.interesting_tasks == 0
2272 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2273 {
2274 waker.wake();
2275 }
2276 }
2277
2278 match cleanup_task {
2279 CleanupTask::Yes => {
2280 if ready_to_delete {
2281 Waitable::Guest(guest_thread.task).delete_from(state)?;
2282 }
2283 }
2284 CleanupTask::No => {}
2285 }
2286
2287 Ok(())
2288 }
2289
2290 fn cancel_guest_subtask_without_lowered_parameters(
2303 &mut self,
2304 caller_instance: RuntimeInstance,
2305 guest_task: TableId<GuestTask>,
2306 ) -> Result<()> {
2307 let concurrent_state = self.concurrent_state_mut()?;
2308 let task = concurrent_state.get_mut(guest_task)?;
2309 assert!(!task.already_lowered_parameters());
2310 task.lower_params = None;
2314 task.lift_result = None;
2315 task.exited = true;
2316 let instance = task.instance;
2317
2318 assert_eq!(1, task.threads.len());
2321 let thread = *task.threads.iter().next().unwrap();
2322 self.cleanup_thread(
2323 QualifiedThreadId {
2324 task: guest_task,
2325 thread,
2326 },
2327 caller_instance,
2328 CleanupTask::No,
2329 )?;
2330
2331 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2333 let pending_count = pending.len();
2334 pending.retain(|thread, _| thread.task != guest_task);
2335 if pending.len() == pending_count {
2337 bail!(Trap::SubtaskCancelAfterTerminal);
2338 }
2339 Ok(())
2340 }
2341
2342 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2345 if !self.concurrency_support() {
2346 return self.current_scope_id_not_concurrent();
2347 }
2348 let (bits, is_host) = match self.current_thread()? {
2349 CurrentThread::Guest(id) => (id.task.rep(), false),
2350 CurrentThread::Host(id) => (id.rep(), true),
2351 CurrentThread::None => return Ok(None),
2352 };
2353 assert_eq!((bits << 1) >> 1, bits);
2354 Ok(Some((bits << 1) | u32::from(is_host)))
2355 }
2356}
2357
2358enum CleanupTask {
2359 Yes,
2360 No,
2361}
2362
2363impl Instance {
2364 fn get_event(
2367 self,
2368 store: &mut StoreOpaque,
2369 guest_task: TableId<GuestTask>,
2370 set: Option<TableId<WaitableSet>>,
2371 cancellable: bool,
2372 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2373 let state = store.concurrent_state_mut()?;
2374
2375 let event = &mut state.get_mut(guest_task)?.event;
2376 if let Some(ev) = event
2377 && (cancellable || !matches!(ev, Event::Cancelled))
2378 {
2379 log::trace!("deliver event {ev:?} to {guest_task:?}");
2380 let ev = *ev;
2381 *event = None;
2382 return Ok(Some((ev, None)));
2383 }
2384
2385 let set = match set {
2386 Some(set) => set,
2387 None => return Ok(None),
2388 };
2389 let waitable = match state.get_mut(set)?.ready.pop_first() {
2390 Some(v) => v,
2391 None => return Ok(None),
2392 };
2393
2394 let common = waitable.common(state)?;
2395 let handle = match common.handle {
2396 Some(h) => h,
2397 None => bail_bug!("handle not set when delivering event"),
2398 };
2399 let event = match common.event.take() {
2400 Some(e) => e,
2401 None => bail_bug!("event not set when delivering event"),
2402 };
2403
2404 log::trace!(
2405 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2406 );
2407
2408 waitable.on_delivery(store, self, event)?;
2409
2410 Ok(Some((event, Some((waitable, handle)))))
2411 }
2412
2413 fn handle_callback_code(
2419 self,
2420 store: &mut StoreOpaque,
2421 guest_thread: QualifiedThreadId,
2422 runtime_instance: RuntimeComponentInstanceIndex,
2423 code: u32,
2424 ) -> Result<Option<GuestCall>> {
2425 let (code, set) = unpack_callback_code(code);
2426
2427 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2428
2429 let state = store.concurrent_state_mut()?;
2430
2431 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2432 let set = store
2433 .instance_state(self.runtime_instance(runtime_instance))
2434 .handle_table()
2435 .waitable_set_rep(handle)?;
2436
2437 Ok(TableId::<WaitableSet>::new(set))
2438 };
2439
2440 Ok(match code {
2441 callback_code::EXIT => {
2442 log::trace!("implicit thread {guest_thread:?} completed");
2443 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2444 task.exited = true;
2445 task.callback = None;
2446 store.cleanup_thread(
2447 guest_thread,
2448 self.runtime_instance(runtime_instance),
2449 CleanupTask::Yes,
2450 )?;
2451 None
2452 }
2453 callback_code::YIELD => {
2454 let task = state.get_mut(guest_thread.task)?;
2455 if let Some(event) = task.event {
2460 assert!(matches!(event, Event::None | Event::Cancelled));
2461 } else {
2462 task.event = Some(Event::None);
2463 }
2464 let call = GuestCall {
2465 thread: guest_thread,
2466 kind: GuestCallKind::DeliverEvent {
2467 instance: self,
2468 set: None,
2469 },
2470 };
2471 if state.may_block(guest_thread.task)? {
2472 state.push_low_priority(WorkItem::GuestCall(runtime_instance, call));
2475 None
2476 } else {
2477 Some(call)
2481 }
2482 }
2483 callback_code::WAIT => {
2484 state.check_blocking_for(guest_thread.task)?;
2487
2488 let set = get_set(store, set)?;
2489 let state = store.concurrent_state_mut()?;
2490
2491 if state.get_mut(guest_thread.task)?.event.is_some()
2492 || !state.get_mut(set)?.ready.is_empty()
2493 {
2494 state.push_high_priority(WorkItem::GuestCall(
2496 runtime_instance,
2497 GuestCall {
2498 thread: guest_thread,
2499 kind: GuestCallKind::DeliverEvent {
2500 instance: self,
2501 set: Some(set),
2502 },
2503 },
2504 ));
2505 } else {
2506 let old = state
2514 .get_mut(guest_thread.thread)?
2515 .wake_on_cancel
2516 .replace(set);
2517 if !old.is_none() {
2518 bail_bug!("thread unexpectedly had wake_on_cancel set");
2519 }
2520 let old = state
2521 .get_mut(set)?
2522 .waiting
2523 .insert(guest_thread, WaitMode::Callback(self));
2524 if !old.is_none() {
2525 bail_bug!("set's waiting set already had this thread registered");
2526 }
2527 }
2528 None
2529 }
2530 _ => bail!(Trap::UnsupportedCallbackCode),
2531 })
2532 }
2533
2534 unsafe fn queue_call<T: 'static>(
2541 self,
2542 mut store: StoreContextMut<T>,
2543 guest_thread: QualifiedThreadId,
2544 callee: SendSyncPtr<VMFuncRef>,
2545 param_count: usize,
2546 result_count: usize,
2547 async_: bool,
2548 callback: Option<SendSyncPtr<VMFuncRef>>,
2549 post_return: Option<SendSyncPtr<VMFuncRef>>,
2550 ) -> Result<()> {
2551 unsafe fn make_call<T: 'static>(
2566 store: StoreContextMut<T>,
2567 guest_thread: QualifiedThreadId,
2568 callee: SendSyncPtr<VMFuncRef>,
2569 param_count: usize,
2570 result_count: usize,
2571 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2572 + Send
2573 + Sync
2574 + 'static
2575 + use<T> {
2576 let token = StoreToken::new(store);
2577 move |store: &mut dyn VMStore| {
2578 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2579
2580 store
2581 .concurrent_state_mut()?
2582 .get_mut(guest_thread.thread)?
2583 .state = GuestThreadState::Running;
2584 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2585 let lower = match task.lower_params.take() {
2586 Some(l) => l,
2587 None => bail_bug!("lower_params missing"),
2588 };
2589
2590 lower(store, &mut storage[..param_count])?;
2591
2592 let mut store = token.as_context_mut(store);
2593
2594 unsafe {
2597 crate::Func::call_unchecked_raw(
2598 &mut store,
2599 callee.as_non_null(),
2600 NonNull::new(
2601 &mut storage[..param_count.max(result_count)]
2602 as *mut [MaybeUninit<ValRaw>] as _,
2603 )
2604 .unwrap(),
2605 )?;
2606 }
2607
2608 Ok(storage)
2609 }
2610 }
2611
2612 let call = unsafe {
2616 make_call(
2617 store.as_context_mut(),
2618 guest_thread,
2619 callee,
2620 param_count,
2621 result_count,
2622 )
2623 };
2624
2625 let callee_instance = store
2626 .0
2627 .concurrent_state_mut()?
2628 .get_mut(guest_thread.task)?
2629 .instance;
2630
2631 let fun = if callback.is_some() {
2632 assert!(async_);
2633
2634 Box::new(move |store: &mut dyn VMStore| {
2635 self.add_guest_thread_to_instance_table(
2636 guest_thread.thread,
2637 store,
2638 callee_instance.index,
2639 )?;
2640 let old_thread = store.set_thread(guest_thread)?;
2641 log::trace!(
2642 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2643 );
2644
2645 store.enter_instance(callee_instance);
2646
2647 let storage = call(store)?;
2654
2655 store.exit_instance(callee_instance)?;
2656
2657 store.set_thread(old_thread)?;
2658 let state = store.concurrent_state_mut()?;
2659 if let Some(t) = old_thread.guest() {
2660 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2661 }
2662 log::trace!("stackless call: restored {old_thread:?} as current thread");
2663
2664 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2667
2668 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2669 })
2670 as Box<dyn FnOnce(&mut dyn VMStore) -> Result<Option<GuestCall>> + Send + Sync>
2671 } else {
2672 let token = StoreToken::new(store.as_context_mut());
2673 Box::new(move |store: &mut dyn VMStore| {
2674 self.add_guest_thread_to_instance_table(
2675 guest_thread.thread,
2676 store,
2677 callee_instance.index,
2678 )?;
2679 let old_thread = store.set_thread(guest_thread)?;
2680 log::trace!(
2681 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2682 );
2683 let flags = self.id().get(store).instance_flags(callee_instance.index);
2684
2685 if !async_ {
2689 store.enter_instance(callee_instance);
2690 }
2691
2692 let storage = call(store)?;
2699
2700 if !async_ {
2701 let lift = {
2707 store.exit_instance(callee_instance)?;
2708
2709 let state = store.concurrent_state_mut()?;
2710 if !state.get_mut(guest_thread.task)?.result.is_none() {
2711 bail_bug!("task has already produced a result");
2712 }
2713
2714 match state.get_mut(guest_thread.task)?.lift_result.take() {
2715 Some(lift) => lift,
2716 None => bail_bug!("lift_result field is missing"),
2717 }
2718 };
2719
2720 let result = (lift.lift)(store, unsafe {
2723 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2724 &storage[..result_count],
2725 )
2726 })?;
2727
2728 let post_return_arg = match result_count {
2729 0 => ValRaw::i32(0),
2730 1 => unsafe { storage[0].assume_init() },
2733 _ => unreachable!(),
2734 };
2735
2736 unsafe {
2737 call_post_return(
2738 token.as_context_mut(store),
2739 post_return.map(|v| v.as_non_null()),
2740 post_return_arg,
2741 flags,
2742 )?;
2743 }
2744
2745 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2746 }
2747
2748 store.set_thread(old_thread)?;
2749
2750 store
2751 .concurrent_state_mut()?
2752 .get_mut(guest_thread.task)?
2753 .exited = true;
2754
2755 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2757 Ok(None)
2758 })
2759 };
2760
2761 store
2762 .0
2763 .concurrent_state_mut()?
2764 .push_high_priority(WorkItem::GuestCall(
2765 callee_instance.index,
2766 GuestCall {
2767 thread: guest_thread,
2768 kind: GuestCallKind::StartImplicit(fun),
2769 },
2770 ));
2771
2772 Ok(())
2773 }
2774
2775 unsafe fn prepare_call<T: 'static>(
2788 self,
2789 mut store: StoreContextMut<T>,
2790 start: NonNull<VMFuncRef>,
2791 return_: NonNull<VMFuncRef>,
2792 caller_instance: RuntimeComponentInstanceIndex,
2793 callee_instance: RuntimeComponentInstanceIndex,
2794 task_return_type: TypeTupleIndex,
2795 callee_async: bool,
2796 memory: *mut VMMemoryDefinition,
2797 string_encoding: StringEncoding,
2798 caller_info: CallerInfo,
2799 ) -> Result<()> {
2800 if let (CallerInfo::Sync { .. }, true) = (&caller_info, callee_async) {
2801 store.0.check_blocking()?;
2805 }
2806
2807 enum ResultInfo {
2808 Heap { results: u32 },
2809 Stack { result_count: u32 },
2810 }
2811
2812 let result_info = match &caller_info {
2813 CallerInfo::Async {
2814 has_result: true,
2815 params,
2816 } => ResultInfo::Heap {
2817 results: match params.last() {
2818 Some(r) => r.get_u32(),
2819 None => bail_bug!("retptr missing"),
2820 },
2821 },
2822 CallerInfo::Async {
2823 has_result: false, ..
2824 } => ResultInfo::Stack { result_count: 0 },
2825 CallerInfo::Sync {
2826 result_count,
2827 params,
2828 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
2829 results: match params.last() {
2830 Some(r) => r.get_u32(),
2831 None => bail_bug!("arg ptr missing"),
2832 },
2833 },
2834 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
2835 result_count: *result_count,
2836 },
2837 };
2838
2839 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
2840
2841 let start = SendSyncPtr::new(start);
2845 let return_ = SendSyncPtr::new(return_);
2846 let token = StoreToken::new(store.as_context_mut());
2847 let old_thread = store.0.current_guest_thread()?;
2848 let state = store.0.concurrent_state_mut()?;
2849
2850 debug_assert_eq!(
2851 state.get_mut(old_thread.task)?.instance,
2852 self.runtime_instance(caller_instance)
2853 );
2854
2855 let guest_thread = GuestTask::new(
2856 state,
2857 Box::new(move |store, dst| {
2858 let mut store = token.as_context_mut(store);
2859 assert!(dst.len() <= MAX_FLAT_PARAMS);
2860 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
2862 let count = match caller_info {
2863 CallerInfo::Async { params, has_result } => {
2867 let params = ¶ms[..params.len() - usize::from(has_result)];
2868 for (param, src) in params.iter().zip(&mut src) {
2869 src.write(*param);
2870 }
2871 params.len()
2872 }
2873
2874 CallerInfo::Sync { params, .. } => {
2876 for (param, src) in params.iter().zip(&mut src) {
2877 src.write(*param);
2878 }
2879 params.len()
2880 }
2881 };
2882 unsafe {
2889 crate::Func::call_unchecked_raw(
2890 &mut store,
2891 start.as_non_null(),
2892 NonNull::new(
2893 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
2894 )
2895 .unwrap(),
2896 )?;
2897 }
2898 dst.copy_from_slice(&src[..dst.len()]);
2899 let task = store.0.current_guest_thread()?.task;
2900 let state = store.0.concurrent_state_mut()?;
2901 Waitable::Guest(task).set_event(
2902 state,
2903 Some(Event::Subtask {
2904 status: Status::Started,
2905 }),
2906 )?;
2907 Ok(())
2908 }),
2909 LiftResult {
2910 lift: Box::new(move |store, src| {
2911 let mut store = token.as_context_mut(store);
2914 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
2916 my_src.push(ValRaw::u32(*results));
2917 }
2918
2919 let prev = store.0.set_thread(old_thread)?;
2925
2926 unsafe {
2933 crate::Func::call_unchecked_raw(
2934 &mut store,
2935 return_.as_non_null(),
2936 my_src.as_mut_slice().into(),
2937 )?;
2938 }
2939
2940 store.0.set_thread(prev)?;
2943
2944 let thread = store.0.current_guest_thread()?;
2945 let state = store.0.concurrent_state_mut()?;
2946 if sync_caller {
2947 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
2948 if let ResultInfo::Stack { result_count } = &result_info {
2949 match result_count {
2950 0 => None,
2951 1 => Some(my_src[0]),
2952 _ => unreachable!(),
2953 }
2954 } else {
2955 None
2956 },
2957 );
2958 }
2959 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
2960 }),
2961 ty: task_return_type,
2962 memory: NonNull::new(memory).map(SendSyncPtr::new),
2963 string_encoding,
2964 },
2965 Caller::Guest { thread: old_thread },
2966 None,
2967 self.runtime_instance(callee_instance),
2968 callee_async,
2969 )?;
2970
2971 store.0.set_thread(guest_thread)?;
2974 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
2975
2976 Ok(())
2977 }
2978
2979 unsafe fn call_callback<T>(
2984 self,
2985 mut store: StoreContextMut<T>,
2986 function: SendSyncPtr<VMFuncRef>,
2987 event: Event,
2988 handle: u32,
2989 ) -> Result<u32> {
2990 let (ordinal, result) = event.parts();
2991 let params = &mut [
2992 ValRaw::u32(ordinal),
2993 ValRaw::u32(handle),
2994 ValRaw::u32(result),
2995 ];
2996 unsafe {
3001 crate::Func::call_unchecked_raw(
3002 &mut store,
3003 function.as_non_null(),
3004 params.as_mut_slice().into(),
3005 )?;
3006 }
3007 Ok(params[0].get_u32())
3008 }
3009
3010 unsafe fn start_call<T: 'static>(
3023 self,
3024 mut store: StoreContextMut<T>,
3025 callback: *mut VMFuncRef,
3026 post_return: *mut VMFuncRef,
3027 callee: NonNull<VMFuncRef>,
3028 param_count: u32,
3029 result_count: u32,
3030 flags: u32,
3031 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3032 ) -> Result<u32> {
3033 let token = StoreToken::new(store.as_context_mut());
3034 let async_caller = storage.is_none();
3035 let guest_thread = store.0.current_guest_thread()?;
3036 let state = store.0.concurrent_state_mut()?;
3037 let callee_async = state.get_mut(guest_thread.task)?.async_function;
3038 let callee = SendSyncPtr::new(callee);
3039 let param_count = usize::try_from(param_count)?;
3040 assert!(param_count <= MAX_FLAT_PARAMS);
3041 let result_count = usize::try_from(result_count)?;
3042 assert!(result_count <= MAX_FLAT_RESULTS);
3043
3044 let task = state.get_mut(guest_thread.task)?;
3045 if let Some(callback) = NonNull::new(callback) {
3046 let callback = SendSyncPtr::new(callback);
3050 task.callback = Some(Box::new(move |store, event, handle| {
3051 let store = token.as_context_mut(store);
3052 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3053 }));
3054 }
3055
3056 let Caller::Guest { thread: caller } = &task.caller else {
3057 bail_bug!("start_call unexpectedly invoked for host->guest call");
3060 };
3061 let caller = *caller;
3062 let caller_instance = state.get_mut(caller.task)?.instance;
3063
3064 unsafe {
3066 self.queue_call(
3067 store.as_context_mut(),
3068 guest_thread,
3069 callee,
3070 param_count,
3071 result_count,
3072 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3073 NonNull::new(callback).map(SendSyncPtr::new),
3074 NonNull::new(post_return).map(SendSyncPtr::new),
3075 )?;
3076 }
3077
3078 let state = store.0.concurrent_state_mut()?;
3079
3080 let guest_waitable = Waitable::Guest(guest_thread.task);
3083 let old_set = guest_waitable.common(state)?.set;
3084 let set = state.get_mut(caller.thread)?.sync_call_set;
3085 guest_waitable.join(state, Some(set))?;
3086
3087 store.0.set_thread(CurrentThread::None)?;
3088
3089 let (status, waitable) = loop {
3105 store.0.suspend(SuspendReason::Waiting {
3106 set,
3107 thread: caller,
3108 skip_may_block_check: async_caller || !callee_async,
3116 })?;
3117
3118 let state = store.0.concurrent_state_mut()?;
3119
3120 log::trace!("taking event for {:?}", guest_thread.task);
3121 let event = guest_waitable.take_event(state)?;
3122 let Some(Event::Subtask { status }) = event else {
3123 bail_bug!("subtasks should only get subtask events, got {event:?}")
3124 };
3125
3126 log::trace!("status {status:?} for {:?}", guest_thread.task);
3127
3128 if status == Status::Returned {
3129 break (status, None);
3131 } else if async_caller {
3132 let handle = store
3136 .0
3137 .instance_state(caller_instance)
3138 .handle_table()
3139 .subtask_insert_guest(guest_thread.task.rep())?;
3140 store
3141 .0
3142 .concurrent_state_mut()?
3143 .get_mut(guest_thread.task)?
3144 .common
3145 .handle = Some(handle);
3146 break (status, Some(handle));
3147 } else {
3148 }
3152 };
3153
3154 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3155
3156 store.0.set_thread(caller)?;
3158 store
3159 .0
3160 .concurrent_state_mut()?
3161 .get_mut(caller.thread)?
3162 .state = GuestThreadState::Running;
3163 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3164
3165 if let Some(storage) = storage {
3166 let state = store.0.concurrent_state_mut()?;
3170 let task = state.get_mut(guest_thread.task)?;
3171 if let Some(result) = task.sync_result.take()? {
3172 if let Some(result) = result {
3173 storage[0] = MaybeUninit::new(result);
3174 }
3175
3176 if task.exited && task.ready_to_delete() {
3177 Waitable::Guest(guest_thread.task).delete_from(state)?;
3178 }
3179 }
3180 }
3181
3182 Ok(status.pack(waitable))
3183 }
3184
3185 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3198 self,
3199 mut store: StoreContextMut<'_, T>,
3200 future: impl Future<Output = Result<R>> + Send + 'static,
3201 lower: impl FnOnce(StoreContextMut<T>, Option<R>) -> Result<()> + Send + 'static,
3202 ) -> Result<Option<u32>> {
3203 let token = StoreToken::new(store.as_context_mut());
3204 let task = store.0.current_host_thread()?;
3205 let state = store.0.concurrent_state_mut()?;
3206
3207 let (join_handle, future) = JoinHandle::run(future);
3210 {
3211 let state = &mut state.get_mut(task)?.state;
3212 assert!(matches!(state, HostTaskState::CalleeStarted));
3213 *state = HostTaskState::CalleeRunning(join_handle);
3214 }
3215
3216 let mut future = Box::pin(future);
3217
3218 let poll = tls::set(store.0, || {
3223 future
3224 .as_mut()
3225 .poll(&mut Context::from_waker(&Waker::noop()))
3226 });
3227
3228 match poll {
3229 Poll::Ready(result) => {
3231 let result = result.transpose()?;
3232 lower(store.as_context_mut(), result)?;
3233 return Ok(None);
3234 }
3235
3236 Poll::Pending => {}
3238 }
3239
3240 let future = Box::pin(async move {
3248 let result = match future.await {
3249 Some(result) => Some(result?),
3250 None => None,
3251 };
3252 let on_complete = move |store: &mut dyn VMStore| {
3253 let mut store = token.as_context_mut(store);
3257 let old = store.0.set_thread(task)?;
3258
3259 let status = if result.is_some() {
3260 Status::Returned
3261 } else {
3262 Status::ReturnCancelled
3263 };
3264
3265 lower(store.as_context_mut(), result)?;
3266 let state = store.0.concurrent_state_mut()?;
3267 match &mut state.get_mut(task)?.state {
3268 HostTaskState::CalleeDone { .. } => {}
3271
3272 other => *other = HostTaskState::CalleeDone { cancelled: false },
3274 }
3275 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3276
3277 store.0.set_thread(old)?;
3278 Ok(())
3279 };
3280
3281 tls::get(move |store| {
3286 store
3287 .concurrent_state_mut()?
3288 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3289 on_complete,
3290 ))));
3291 Ok(())
3292 })
3293 });
3294
3295 let state = store.0.concurrent_state_mut()?;
3298 state.push_future(future);
3299 let caller = state.get_mut(task)?.caller;
3300 let instance = state.get_mut(caller.task)?.instance;
3301 let handle = store
3302 .0
3303 .instance_state(instance)
3304 .handle_table()
3305 .subtask_insert_host(task.rep())?;
3306 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3307 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3308
3309 store.0.set_thread(caller)?;
3313 Ok(Some(handle))
3314 }
3315
3316 pub(crate) fn task_return(
3319 self,
3320 store: &mut dyn VMStore,
3321 ty: TypeTupleIndex,
3322 options: OptionsIndex,
3323 storage: &[ValRaw],
3324 ) -> Result<()> {
3325 let guest_thread = store.current_guest_thread()?;
3326 let state = store.concurrent_state_mut()?;
3327 let lift = state
3328 .get_mut(guest_thread.task)?
3329 .lift_result
3330 .take()
3331 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3332 if !state.get_mut(guest_thread.task)?.result.is_none() {
3333 bail_bug!("task result unexpectedly already set");
3334 }
3335
3336 let CanonicalOptions {
3337 string_encoding,
3338 data_model,
3339 ..
3340 } = &self.id().get(store).component().env_component().options[options];
3341
3342 let invalid = ty != lift.ty
3343 || string_encoding != &lift.string_encoding
3344 || match data_model {
3345 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3346 Some(memory) => {
3347 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3348 let actual = self.id().get(store).runtime_memory(memory);
3349 expected != actual.as_ptr()
3350 }
3351 None => false,
3354 },
3355 CanonicalOptionsDataModel::Gc { .. } => true,
3357 };
3358
3359 if invalid {
3360 bail!(Trap::TaskReturnInvalid);
3361 }
3362
3363 log::trace!("task.return for {guest_thread:?}");
3364
3365 let result = (lift.lift)(store, storage)?;
3366 self.task_complete(store, guest_thread.task, result, Status::Returned)
3367 }
3368
3369 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3371 let guest_thread = store.current_guest_thread()?;
3372 let state = store.concurrent_state_mut()?;
3373 let task = state.get_mut(guest_thread.task)?;
3374 if !task.cancel_sent {
3375 bail!(Trap::TaskCancelNotCancelled);
3376 }
3377 _ = task
3378 .lift_result
3379 .take()
3380 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3381
3382 if !task.result.is_none() {
3383 bail_bug!("task result should not bet set yet");
3384 }
3385
3386 log::trace!("task.cancel for {guest_thread:?}");
3387
3388 self.task_complete(
3389 store,
3390 guest_thread.task,
3391 Box::new(DummyResult),
3392 Status::ReturnCancelled,
3393 )
3394 }
3395
3396 fn task_complete(
3402 self,
3403 store: &mut StoreOpaque,
3404 guest_task: TableId<GuestTask>,
3405 result: Box<dyn Any + Send + Sync>,
3406 status: Status,
3407 ) -> Result<()> {
3408 store
3409 .component_resource_tables(Some(self))?
3410 .validate_scope_exit()?;
3411
3412 let state = store.concurrent_state_mut()?;
3413 let task = state.get_mut(guest_task)?;
3414
3415 if let Caller::Host { tx, .. } = &mut task.caller {
3416 if let Some(tx) = tx.take() {
3417 _ = tx.send(result);
3418 }
3419 } else {
3420 task.result = Some(result);
3421 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3422 }
3423
3424 Ok(())
3425 }
3426
3427 pub(crate) fn waitable_set_new(
3429 self,
3430 store: &mut StoreOpaque,
3431 caller_instance: RuntimeComponentInstanceIndex,
3432 ) -> Result<u32> {
3433 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3434 let handle = store
3435 .instance_state(self.runtime_instance(caller_instance))
3436 .handle_table()
3437 .waitable_set_insert(set.rep())?;
3438 log::trace!("new waitable set {set:?} (handle {handle})");
3439 Ok(handle)
3440 }
3441
3442 pub(crate) fn waitable_set_drop(
3444 self,
3445 store: &mut StoreOpaque,
3446 caller_instance: RuntimeComponentInstanceIndex,
3447 set: u32,
3448 ) -> Result<()> {
3449 let rep = store
3450 .instance_state(self.runtime_instance(caller_instance))
3451 .handle_table()
3452 .waitable_set_remove(set)?;
3453
3454 log::trace!("drop waitable set {rep} (handle {set})");
3455
3456 if !store
3460 .concurrent_state_mut()?
3461 .get_mut(TableId::<WaitableSet>::new(rep))?
3462 .waiting
3463 .is_empty()
3464 {
3465 bail!(Trap::WaitableSetDropHasWaiters);
3466 }
3467
3468 store
3469 .concurrent_state_mut()?
3470 .delete(TableId::<WaitableSet>::new(rep))?;
3471
3472 Ok(())
3473 }
3474
3475 pub(crate) fn waitable_join(
3477 self,
3478 store: &mut StoreOpaque,
3479 caller_instance: RuntimeComponentInstanceIndex,
3480 waitable_handle: u32,
3481 set_handle: u32,
3482 ) -> Result<()> {
3483 let mut instance = self.id().get_mut(store);
3484 let waitable =
3485 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3486
3487 let set = if set_handle == 0 {
3488 None
3489 } else {
3490 let set = instance.instance_states().0[caller_instance]
3491 .handle_table()
3492 .waitable_set_rep(set_handle)?;
3493
3494 let state = store.concurrent_state_mut()?;
3495 if let Some(old) = waitable.common(state)?.set
3496 && state.get_mut(old)?.is_sync_call_set
3497 {
3498 bail!(Trap::WaitableSyncAndAsync);
3499 }
3500
3501 Some(TableId::<WaitableSet>::new(set))
3502 };
3503
3504 log::trace!(
3505 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3506 );
3507
3508 waitable.join(store.concurrent_state_mut()?, set)
3509 }
3510
3511 pub(crate) fn subtask_drop(
3513 self,
3514 store: &mut StoreOpaque,
3515 caller_instance: RuntimeComponentInstanceIndex,
3516 task_id: u32,
3517 ) -> Result<()> {
3518 self.waitable_join(store, caller_instance, task_id, 0)?;
3519
3520 let (rep, is_host) = store
3521 .instance_state(self.runtime_instance(caller_instance))
3522 .handle_table()
3523 .subtask_remove(task_id)?;
3524
3525 let concurrent_state = store.concurrent_state_mut()?;
3526 let (waitable, delete) = if is_host {
3527 let id = TableId::<HostTask>::new(rep);
3528 let task = concurrent_state.get_mut(id)?;
3529 match &task.state {
3530 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3531 HostTaskState::CalleeDone { .. } => {}
3532 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3533 bail_bug!("invalid state for callee in `subtask.drop`")
3534 }
3535 }
3536 (Waitable::Host(id), true)
3537 } else {
3538 let id = TableId::<GuestTask>::new(rep);
3539 let task = concurrent_state.get_mut(id)?;
3540 if task.lift_result.is_some() {
3541 bail!(Trap::SubtaskDropNotResolved);
3542 }
3543 (
3544 Waitable::Guest(id),
3545 concurrent_state.get_mut(id)?.ready_to_delete(),
3546 )
3547 };
3548
3549 waitable.common(concurrent_state)?.handle = None;
3550
3551 if waitable.take_event(concurrent_state)?.is_some() {
3554 bail!(Trap::SubtaskDropNotResolved);
3555 }
3556
3557 if delete {
3558 waitable.delete_from(concurrent_state)?;
3559 }
3560
3561 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3562 Ok(())
3563 }
3564
3565 pub(crate) fn waitable_set_wait(
3567 self,
3568 store: &mut StoreOpaque,
3569 options: OptionsIndex,
3570 set: u32,
3571 payload: u32,
3572 ) -> Result<u32> {
3573 if !self.options(store, options).async_ {
3574 store.check_blocking()?;
3578 }
3579
3580 let &CanonicalOptions {
3581 cancellable,
3582 instance: caller_instance,
3583 ..
3584 } = &self.id().get(store).component().env_component().options[options];
3585 let rep = store
3586 .instance_state(self.runtime_instance(caller_instance))
3587 .handle_table()
3588 .waitable_set_rep(set)?;
3589
3590 self.waitable_check(
3591 store,
3592 cancellable,
3593 WaitableCheck::Wait,
3594 WaitableCheckParams {
3595 set: TableId::new(rep),
3596 options,
3597 payload,
3598 },
3599 )
3600 }
3601
3602 pub(crate) fn waitable_set_poll(
3604 self,
3605 store: &mut StoreOpaque,
3606 options: OptionsIndex,
3607 set: u32,
3608 payload: u32,
3609 ) -> Result<u32> {
3610 let &CanonicalOptions {
3611 cancellable,
3612 instance: caller_instance,
3613 ..
3614 } = &self.id().get(store).component().env_component().options[options];
3615 let rep = store
3616 .instance_state(self.runtime_instance(caller_instance))
3617 .handle_table()
3618 .waitable_set_rep(set)?;
3619
3620 self.waitable_check(
3621 store,
3622 cancellable,
3623 WaitableCheck::Poll,
3624 WaitableCheckParams {
3625 set: TableId::new(rep),
3626 options,
3627 payload,
3628 },
3629 )
3630 }
3631
3632 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3634 let thread_id = store.current_guest_thread()?.thread;
3635 match store
3636 .concurrent_state_mut()?
3637 .get_mut(thread_id)?
3638 .instance_rep
3639 {
3640 Some(r) => Ok(r),
3641 None => bail_bug!("thread should have instance_rep by now"),
3642 }
3643 }
3644
3645 pub(crate) fn thread_new_indirect<T: 'static>(
3647 self,
3648 mut store: StoreContextMut<T>,
3649 runtime_instance: RuntimeComponentInstanceIndex,
3650 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3652 start_func_idx: u32,
3653 context: i32,
3654 ) -> Result<u32> {
3655 log::trace!("creating new thread");
3656
3657 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3658 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3659 let callee = instance
3660 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3661 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3662 if callee.type_index(store.0) != start_func_ty.type_index() {
3663 bail!(Trap::ThreadNewIndirectInvalidType);
3664 }
3665
3666 let token = StoreToken::new(store.as_context_mut());
3667 let start_func = Box::new(
3668 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3669 let old_thread = store.set_thread(guest_thread)?;
3670 log::trace!(
3671 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3672 );
3673
3674 let mut store = token.as_context_mut(store);
3675 let mut params = [ValRaw::i32(context)];
3676 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3679
3680 store.0.set_thread(old_thread)?;
3681
3682 store.0.cleanup_thread(
3683 guest_thread,
3684 self.runtime_instance(runtime_instance),
3685 CleanupTask::Yes,
3686 )?;
3687 log::trace!("explicit thread {guest_thread:?} completed");
3688 let state = store.0.concurrent_state_mut()?;
3689 if let Some(t) = old_thread.guest() {
3690 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3691 }
3692 log::trace!("thread start: restored {old_thread:?} as current thread");
3693
3694 Ok(())
3695 },
3696 );
3697
3698 let current_thread = store.0.current_guest_thread()?;
3699 let state = store.0.concurrent_state_mut()?;
3700 let parent_task = current_thread.task;
3701
3702 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3703 let thread_id = state.push(new_thread)?;
3704 state.get_mut(parent_task)?.threads.insert(thread_id);
3705
3706 log::trace!("new thread with id {thread_id:?} created");
3707
3708 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3709 }
3710
3711 pub(crate) fn resume_thread(
3712 self,
3713 store: &mut StoreOpaque,
3714 runtime_instance: RuntimeComponentInstanceIndex,
3715 thread_idx: u32,
3716 high_priority: bool,
3717 allow_ready: bool,
3718 ) -> Result<()> {
3719 let thread_id =
3720 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3721 let state = store.concurrent_state_mut()?;
3722 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3723 let thread = state.get_mut(guest_thread.thread)?;
3724
3725 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3726 GuestThreadState::NotStartedExplicit(start_func) => {
3727 log::trace!("starting thread {guest_thread:?}");
3728 let guest_call = WorkItem::GuestCall(
3729 runtime_instance,
3730 GuestCall {
3731 thread: guest_thread,
3732 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3733 start_func(store, guest_thread)
3734 })),
3735 },
3736 );
3737 store
3738 .concurrent_state_mut()?
3739 .push_work_item(guest_call, high_priority);
3740 }
3741 GuestThreadState::Suspended(fiber) => {
3742 log::trace!("resuming thread {thread_id:?} that was suspended");
3743 store
3744 .concurrent_state_mut()?
3745 .push_work_item(WorkItem::ResumeFiber(fiber), high_priority);
3746 }
3747 GuestThreadState::Ready { fiber, cancellable } if allow_ready => {
3748 log::trace!("resuming thread {thread_id:?} that was ready");
3749 thread.state = GuestThreadState::Ready { fiber, cancellable };
3750 store
3751 .concurrent_state_mut()?
3752 .promote_thread_work_item(guest_thread);
3753 }
3754 other => {
3755 thread.state = other;
3756 bail!(Trap::CannotResumeThread);
3757 }
3758 }
3759 Ok(())
3760 }
3761
3762 fn add_guest_thread_to_instance_table(
3763 self,
3764 thread_id: TableId<GuestThread>,
3765 store: &mut StoreOpaque,
3766 runtime_instance: RuntimeComponentInstanceIndex,
3767 ) -> Result<u32> {
3768 let guest_id = store
3769 .instance_state(self.runtime_instance(runtime_instance))
3770 .thread_handle_table()
3771 .guest_thread_insert(thread_id.rep())?;
3772 store
3773 .concurrent_state_mut()?
3774 .get_mut(thread_id)?
3775 .instance_rep = Some(guest_id);
3776 Ok(guest_id)
3777 }
3778
3779 pub(crate) fn suspension_intrinsic(
3782 self,
3783 store: &mut StoreOpaque,
3784 caller: RuntimeComponentInstanceIndex,
3785 cancellable: bool,
3786 yielding: bool,
3787 to_thread: SuspensionTarget,
3788 ) -> Result<WaitResult> {
3789 let guest_thread = store.current_guest_thread()?;
3790 if to_thread.is_none() {
3791 let state = store.concurrent_state_mut()?;
3792 if yielding {
3793 if !state.may_block(guest_thread.task)? {
3795 if !state.promote_instance_local_thread_work_item(caller) {
3798 return Ok(WaitResult::Completed);
3800 }
3801 }
3802 } else {
3803 store.check_blocking()?;
3807 }
3808 }
3809
3810 if cancellable && store.take_pending_cancellation()? {
3812 return Ok(WaitResult::Cancelled);
3813 }
3814
3815 match to_thread {
3816 SuspensionTarget::SomeSuspended(thread) => {
3817 self.resume_thread(store, caller, thread, true, false)?
3818 }
3819 SuspensionTarget::Some(thread) => {
3820 self.resume_thread(store, caller, thread, true, true)?
3821 }
3822 SuspensionTarget::None => { }
3823 }
3824
3825 let reason = if yielding {
3826 SuspendReason::Yielding {
3827 thread: guest_thread,
3828 cancellable,
3829 skip_may_block_check: to_thread.is_some(),
3833 }
3834 } else {
3835 SuspendReason::ExplicitlySuspending {
3836 thread: guest_thread,
3837 skip_may_block_check: to_thread.is_some(),
3841 }
3842 };
3843
3844 store.suspend(reason)?;
3845
3846 if cancellable && store.take_pending_cancellation()? {
3847 Ok(WaitResult::Cancelled)
3848 } else {
3849 Ok(WaitResult::Completed)
3850 }
3851 }
3852
3853 fn waitable_check(
3855 self,
3856 store: &mut StoreOpaque,
3857 cancellable: bool,
3858 check: WaitableCheck,
3859 params: WaitableCheckParams,
3860 ) -> Result<u32> {
3861 let guest_thread = store.current_guest_thread()?;
3862
3863 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
3864
3865 let state = store.concurrent_state_mut()?;
3866 let task = state.get_mut(guest_thread.task)?;
3867
3868 match &check {
3871 WaitableCheck::Wait => {
3872 let set = params.set;
3873
3874 if (task.event.is_none()
3875 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
3876 && state.get_mut(set)?.ready.is_empty()
3877 {
3878 if cancellable {
3879 let old = state
3880 .get_mut(guest_thread.thread)?
3881 .wake_on_cancel
3882 .replace(set);
3883 if !old.is_none() {
3884 bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
3885 }
3886 }
3887
3888 store.suspend(SuspendReason::Waiting {
3889 set,
3890 thread: guest_thread,
3891 skip_may_block_check: false,
3892 })?;
3893 }
3894 }
3895 WaitableCheck::Poll => {}
3896 }
3897
3898 log::trace!(
3899 "waitable check for {guest_thread:?}; set {:?}, part two",
3900 params.set
3901 );
3902
3903 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
3905
3906 let (ordinal, handle, result) = match &check {
3907 WaitableCheck::Wait => {
3908 let (event, waitable) = match event {
3909 Some(p) => p,
3910 None => bail_bug!("event expected to be present"),
3911 };
3912 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3913 let (ordinal, result) = event.parts();
3914 (ordinal, handle, result)
3915 }
3916 WaitableCheck::Poll => {
3917 if let Some((event, waitable)) = event {
3918 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
3919 let (ordinal, result) = event.parts();
3920 (ordinal, handle, result)
3921 } else {
3922 log::trace!(
3923 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
3924 guest_thread.task,
3925 params.set
3926 );
3927 let (ordinal, result) = Event::None.parts();
3928 (ordinal, 0, result)
3929 }
3930 }
3931 };
3932 let memory = self.options_memory_mut(store, params.options);
3933 let ptr = crate::component::func::validate_inbounds_dynamic(
3934 &CanonicalAbiInfo::POINTER_PAIR,
3935 memory,
3936 &ValRaw::u32(params.payload),
3937 )?;
3938 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
3939 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
3940 Ok(ordinal)
3941 }
3942
3943 pub(crate) fn subtask_cancel(
3945 self,
3946 store: &mut StoreOpaque,
3947 caller_instance: RuntimeComponentInstanceIndex,
3948 async_: bool,
3949 task_id: u32,
3950 ) -> Result<u32> {
3951 if !async_ {
3952 store.check_blocking()?;
3956 }
3957
3958 let (rep, is_host) = store
3959 .instance_state(self.runtime_instance(caller_instance))
3960 .handle_table()
3961 .subtask_rep(task_id)?;
3962 let waitable = if is_host {
3963 Waitable::Host(TableId::<HostTask>::new(rep))
3964 } else {
3965 Waitable::Guest(TableId::<GuestTask>::new(rep))
3966 };
3967 let concurrent_state = store.concurrent_state_mut()?;
3968
3969 log::trace!("subtask_cancel {waitable:?} (handle {task_id})");
3970
3971 if !async_ {
3972 waitable.trap_if_in_waitable_set(concurrent_state)?;
3973 }
3974
3975 let needs_block;
3976 if let Waitable::Host(host_task) = waitable {
3977 let state = &mut concurrent_state.get_mut(host_task)?.state;
3978 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
3979 HostTaskState::CalleeRunning(handle) => {
3986 handle.abort();
3987 needs_block = true;
3988 }
3989
3990 HostTaskState::CalleeDone { cancelled } => {
3993 if cancelled {
3994 bail!(Trap::SubtaskCancelAfterTerminal);
3995 } else {
3996 needs_block = false;
3999 }
4000 }
4001
4002 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4005 bail_bug!("invalid states for host callee")
4006 }
4007 }
4008 } else {
4009 let guest_task = TableId::<GuestTask>::new(rep);
4010 let task = concurrent_state.get_mut(guest_task)?;
4011 if !task.already_lowered_parameters() {
4012 store.cancel_guest_subtask_without_lowered_parameters(
4013 self.runtime_instance(caller_instance),
4014 guest_task,
4015 )?;
4016 return Ok(Status::StartCancelled as u32);
4017 } else if !task.returned_or_cancelled() {
4018 task.cancel_sent = true;
4021 task.event = Some(Event::Cancelled);
4026 let runtime_instance = task.instance.index;
4027 for thread in task.threads.clone() {
4028 let thread = QualifiedThreadId {
4029 task: guest_task,
4030 thread,
4031 };
4032 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4033 if let Some(set) = thread_mut.wake_on_cancel.take() {
4034 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4036 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber(fiber),
4037 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall(
4038 runtime_instance,
4039 GuestCall {
4040 thread,
4041 kind: GuestCallKind::DeliverEvent {
4042 instance,
4043 set: None,
4044 },
4045 },
4046 ),
4047 None => bail_bug!("thread not present in wake_on_cancel set"),
4048 };
4049 concurrent_state.push_high_priority(item);
4050
4051 let caller = store.current_guest_thread()?;
4052 store.suspend(SuspendReason::Yielding {
4053 thread: caller,
4054 cancellable: false,
4055 skip_may_block_check: false,
4058 })?;
4059 break;
4060 } else if let GuestThreadState::Ready {
4061 cancellable: true, ..
4062 } = &thread_mut.state
4063 {
4064 concurrent_state.promote_thread_work_item(thread);
4067 let caller = store.current_guest_thread()?;
4068 store.suspend(SuspendReason::Yielding {
4069 thread: caller,
4070 cancellable: false,
4071 skip_may_block_check: false,
4072 })?;
4073 break;
4074 }
4075 }
4076
4077 needs_block = !store
4080 .concurrent_state_mut()?
4081 .get_mut(guest_task)?
4082 .returned_or_cancelled()
4083 } else {
4084 needs_block = false;
4085 }
4086 };
4087
4088 if needs_block {
4092 if async_ {
4093 return Ok(BLOCKED);
4094 }
4095
4096 store.wait_for_event(waitable)?;
4100
4101 }
4103
4104 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4105 if let Some(Event::Subtask {
4106 status: status @ (Status::Returned | Status::ReturnCancelled),
4107 }) = event
4108 {
4109 Ok(status as u32)
4110 } else {
4111 bail!(Trap::SubtaskCancelAfterTerminal);
4112 }
4113 }
4114}
4115
4116pub trait VMComponentAsyncStore {
4124 unsafe fn prepare_call(
4130 &mut self,
4131 instance: Instance,
4132 memory: *mut VMMemoryDefinition,
4133 start: NonNull<VMFuncRef>,
4134 return_: NonNull<VMFuncRef>,
4135 caller_instance: RuntimeComponentInstanceIndex,
4136 callee_instance: RuntimeComponentInstanceIndex,
4137 task_return_type: TypeTupleIndex,
4138 callee_async: bool,
4139 string_encoding: StringEncoding,
4140 result_count: u32,
4141 storage: *mut ValRaw,
4142 storage_len: usize,
4143 ) -> Result<()>;
4144
4145 unsafe fn sync_start(
4148 &mut self,
4149 instance: Instance,
4150 callback: *mut VMFuncRef,
4151 callee: NonNull<VMFuncRef>,
4152 param_count: u32,
4153 storage: *mut MaybeUninit<ValRaw>,
4154 storage_len: usize,
4155 ) -> Result<()>;
4156
4157 unsafe fn async_start(
4160 &mut self,
4161 instance: Instance,
4162 callback: *mut VMFuncRef,
4163 post_return: *mut VMFuncRef,
4164 callee: NonNull<VMFuncRef>,
4165 param_count: u32,
4166 result_count: u32,
4167 flags: u32,
4168 ) -> Result<u32>;
4169
4170 fn future_write(
4172 &mut self,
4173 instance: Instance,
4174 caller: RuntimeComponentInstanceIndex,
4175 ty: TypeFutureTableIndex,
4176 options: OptionsIndex,
4177 future: u32,
4178 address: u32,
4179 ) -> Result<u32>;
4180
4181 fn future_read(
4183 &mut self,
4184 instance: Instance,
4185 caller: RuntimeComponentInstanceIndex,
4186 ty: TypeFutureTableIndex,
4187 options: OptionsIndex,
4188 future: u32,
4189 address: u32,
4190 ) -> Result<u32>;
4191
4192 fn future_drop_writable(
4194 &mut self,
4195 instance: Instance,
4196 ty: TypeFutureTableIndex,
4197 writer: u32,
4198 ) -> Result<()>;
4199
4200 fn stream_write(
4202 &mut self,
4203 instance: Instance,
4204 caller: RuntimeComponentInstanceIndex,
4205 ty: TypeStreamTableIndex,
4206 options: OptionsIndex,
4207 stream: u32,
4208 address: u32,
4209 count: u32,
4210 ) -> Result<u32>;
4211
4212 fn stream_read(
4214 &mut self,
4215 instance: Instance,
4216 caller: RuntimeComponentInstanceIndex,
4217 ty: TypeStreamTableIndex,
4218 options: OptionsIndex,
4219 stream: u32,
4220 address: u32,
4221 count: u32,
4222 ) -> Result<u32>;
4223
4224 fn flat_stream_write(
4227 &mut self,
4228 instance: Instance,
4229 caller: RuntimeComponentInstanceIndex,
4230 ty: TypeStreamTableIndex,
4231 options: OptionsIndex,
4232 payload_size: u32,
4233 payload_align: u32,
4234 stream: u32,
4235 address: u32,
4236 count: u32,
4237 ) -> Result<u32>;
4238
4239 fn flat_stream_read(
4242 &mut self,
4243 instance: Instance,
4244 caller: RuntimeComponentInstanceIndex,
4245 ty: TypeStreamTableIndex,
4246 options: OptionsIndex,
4247 payload_size: u32,
4248 payload_align: u32,
4249 stream: u32,
4250 address: u32,
4251 count: u32,
4252 ) -> Result<u32>;
4253
4254 fn stream_drop_writable(
4256 &mut self,
4257 instance: Instance,
4258 ty: TypeStreamTableIndex,
4259 writer: u32,
4260 ) -> Result<()>;
4261
4262 fn error_context_debug_message(
4264 &mut self,
4265 instance: Instance,
4266 ty: TypeComponentLocalErrorContextTableIndex,
4267 options: OptionsIndex,
4268 err_ctx_handle: u32,
4269 debug_msg_address: u32,
4270 ) -> Result<()>;
4271
4272 fn thread_new_indirect(
4274 &mut self,
4275 instance: Instance,
4276 caller: RuntimeComponentInstanceIndex,
4277 func_ty_idx: TypeFuncIndex,
4278 start_func_table_idx: RuntimeTableIndex,
4279 start_func_idx: u32,
4280 context: i32,
4281 ) -> Result<u32>;
4282}
4283
4284impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4286 unsafe fn prepare_call(
4287 &mut self,
4288 instance: Instance,
4289 memory: *mut VMMemoryDefinition,
4290 start: NonNull<VMFuncRef>,
4291 return_: NonNull<VMFuncRef>,
4292 caller_instance: RuntimeComponentInstanceIndex,
4293 callee_instance: RuntimeComponentInstanceIndex,
4294 task_return_type: TypeTupleIndex,
4295 callee_async: bool,
4296 string_encoding: StringEncoding,
4297 result_count_or_max_if_async: u32,
4298 storage: *mut ValRaw,
4299 storage_len: usize,
4300 ) -> Result<()> {
4301 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4305
4306 unsafe {
4307 instance.prepare_call(
4308 StoreContextMut(self),
4309 start,
4310 return_,
4311 caller_instance,
4312 callee_instance,
4313 task_return_type,
4314 callee_async,
4315 memory,
4316 string_encoding,
4317 match result_count_or_max_if_async {
4318 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4319 params,
4320 has_result: false,
4321 },
4322 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4323 params,
4324 has_result: true,
4325 },
4326 result_count => CallerInfo::Sync {
4327 params,
4328 result_count,
4329 },
4330 },
4331 )
4332 }
4333 }
4334
4335 unsafe fn sync_start(
4336 &mut self,
4337 instance: Instance,
4338 callback: *mut VMFuncRef,
4339 callee: NonNull<VMFuncRef>,
4340 param_count: u32,
4341 storage: *mut MaybeUninit<ValRaw>,
4342 storage_len: usize,
4343 ) -> Result<()> {
4344 unsafe {
4345 instance
4346 .start_call(
4347 StoreContextMut(self),
4348 callback,
4349 ptr::null_mut(),
4350 callee,
4351 param_count,
4352 1,
4353 START_FLAG_ASYNC_CALLEE,
4354 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4358 )
4359 .map(drop)
4360 }
4361 }
4362
4363 unsafe fn async_start(
4364 &mut self,
4365 instance: Instance,
4366 callback: *mut VMFuncRef,
4367 post_return: *mut VMFuncRef,
4368 callee: NonNull<VMFuncRef>,
4369 param_count: u32,
4370 result_count: u32,
4371 flags: u32,
4372 ) -> Result<u32> {
4373 unsafe {
4374 instance.start_call(
4375 StoreContextMut(self),
4376 callback,
4377 post_return,
4378 callee,
4379 param_count,
4380 result_count,
4381 flags,
4382 None,
4383 )
4384 }
4385 }
4386
4387 fn future_write(
4388 &mut self,
4389 instance: Instance,
4390 caller: RuntimeComponentInstanceIndex,
4391 ty: TypeFutureTableIndex,
4392 options: OptionsIndex,
4393 future: u32,
4394 address: u32,
4395 ) -> Result<u32> {
4396 instance
4397 .guest_write(
4398 StoreContextMut(self),
4399 caller,
4400 TransmitIndex::Future(ty),
4401 options,
4402 None,
4403 future,
4404 address,
4405 1,
4406 )
4407 .map(|result| result.encode())
4408 }
4409
4410 fn future_read(
4411 &mut self,
4412 instance: Instance,
4413 caller: RuntimeComponentInstanceIndex,
4414 ty: TypeFutureTableIndex,
4415 options: OptionsIndex,
4416 future: u32,
4417 address: u32,
4418 ) -> Result<u32> {
4419 instance
4420 .guest_read(
4421 StoreContextMut(self),
4422 caller,
4423 TransmitIndex::Future(ty),
4424 options,
4425 None,
4426 future,
4427 address,
4428 1,
4429 )
4430 .map(|result| result.encode())
4431 }
4432
4433 fn stream_write(
4434 &mut self,
4435 instance: Instance,
4436 caller: RuntimeComponentInstanceIndex,
4437 ty: TypeStreamTableIndex,
4438 options: OptionsIndex,
4439 stream: u32,
4440 address: u32,
4441 count: u32,
4442 ) -> Result<u32> {
4443 instance
4444 .guest_write(
4445 StoreContextMut(self),
4446 caller,
4447 TransmitIndex::Stream(ty),
4448 options,
4449 None,
4450 stream,
4451 address,
4452 count,
4453 )
4454 .map(|result| result.encode())
4455 }
4456
4457 fn stream_read(
4458 &mut self,
4459 instance: Instance,
4460 caller: RuntimeComponentInstanceIndex,
4461 ty: TypeStreamTableIndex,
4462 options: OptionsIndex,
4463 stream: u32,
4464 address: u32,
4465 count: u32,
4466 ) -> Result<u32> {
4467 instance
4468 .guest_read(
4469 StoreContextMut(self),
4470 caller,
4471 TransmitIndex::Stream(ty),
4472 options,
4473 None,
4474 stream,
4475 address,
4476 count,
4477 )
4478 .map(|result| result.encode())
4479 }
4480
4481 fn future_drop_writable(
4482 &mut self,
4483 instance: Instance,
4484 ty: TypeFutureTableIndex,
4485 writer: u32,
4486 ) -> Result<()> {
4487 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4488 }
4489
4490 fn flat_stream_write(
4491 &mut self,
4492 instance: Instance,
4493 caller: RuntimeComponentInstanceIndex,
4494 ty: TypeStreamTableIndex,
4495 options: OptionsIndex,
4496 payload_size: u32,
4497 payload_align: u32,
4498 stream: u32,
4499 address: u32,
4500 count: u32,
4501 ) -> Result<u32> {
4502 instance
4503 .guest_write(
4504 StoreContextMut(self),
4505 caller,
4506 TransmitIndex::Stream(ty),
4507 options,
4508 Some(FlatAbi {
4509 size: payload_size,
4510 align: payload_align,
4511 }),
4512 stream,
4513 address,
4514 count,
4515 )
4516 .map(|result| result.encode())
4517 }
4518
4519 fn flat_stream_read(
4520 &mut self,
4521 instance: Instance,
4522 caller: RuntimeComponentInstanceIndex,
4523 ty: TypeStreamTableIndex,
4524 options: OptionsIndex,
4525 payload_size: u32,
4526 payload_align: u32,
4527 stream: u32,
4528 address: u32,
4529 count: u32,
4530 ) -> Result<u32> {
4531 instance
4532 .guest_read(
4533 StoreContextMut(self),
4534 caller,
4535 TransmitIndex::Stream(ty),
4536 options,
4537 Some(FlatAbi {
4538 size: payload_size,
4539 align: payload_align,
4540 }),
4541 stream,
4542 address,
4543 count,
4544 )
4545 .map(|result| result.encode())
4546 }
4547
4548 fn stream_drop_writable(
4549 &mut self,
4550 instance: Instance,
4551 ty: TypeStreamTableIndex,
4552 writer: u32,
4553 ) -> Result<()> {
4554 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4555 }
4556
4557 fn error_context_debug_message(
4558 &mut self,
4559 instance: Instance,
4560 ty: TypeComponentLocalErrorContextTableIndex,
4561 options: OptionsIndex,
4562 err_ctx_handle: u32,
4563 debug_msg_address: u32,
4564 ) -> Result<()> {
4565 instance.error_context_debug_message(
4566 StoreContextMut(self),
4567 ty,
4568 options,
4569 err_ctx_handle,
4570 debug_msg_address,
4571 )
4572 }
4573
4574 fn thread_new_indirect(
4575 &mut self,
4576 instance: Instance,
4577 caller: RuntimeComponentInstanceIndex,
4578 func_ty_idx: TypeFuncIndex,
4579 start_func_table_idx: RuntimeTableIndex,
4580 start_func_idx: u32,
4581 context: i32,
4582 ) -> Result<u32> {
4583 instance.thread_new_indirect(
4584 StoreContextMut(self),
4585 caller,
4586 func_ty_idx,
4587 start_func_table_idx,
4588 start_func_idx,
4589 context,
4590 )
4591 }
4592}
4593
4594type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4595
4596pub(crate) struct HostTask {
4600 common: WaitableCommon,
4601
4602 caller: QualifiedThreadId,
4604
4605 call_context: CallContext,
4608
4609 state: HostTaskState,
4610}
4611
4612enum HostTaskState {
4613 CalleeStarted,
4618
4619 CalleeRunning(JoinHandle),
4624
4625 CalleeFinished(LiftedResult),
4629
4630 CalleeDone { cancelled: bool },
4633}
4634
4635impl HostTask {
4636 fn new(caller: QualifiedThreadId, state: HostTaskState) -> Self {
4637 Self {
4638 common: WaitableCommon::default(),
4639 call_context: CallContext::default(),
4640 caller,
4641 state,
4642 }
4643 }
4644}
4645
4646impl TableDebug for HostTask {
4647 fn type_name() -> &'static str {
4648 "HostTask"
4649 }
4650}
4651
4652type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4653
4654enum Caller {
4656 Host {
4658 tx: Option<oneshot::Sender<LiftedResult>>,
4660 host_future_present: bool,
4663 caller: CurrentThread,
4667 },
4668 Guest {
4670 thread: QualifiedThreadId,
4672 },
4673}
4674
4675struct LiftResult {
4678 lift: RawLift,
4679 ty: TypeTupleIndex,
4680 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4681 string_encoding: StringEncoding,
4682}
4683
4684#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4689pub(crate) struct QualifiedThreadId {
4690 task: TableId<GuestTask>,
4691 thread: TableId<GuestThread>,
4692}
4693
4694impl QualifiedThreadId {
4695 fn qualify(
4696 state: &mut ConcurrentState,
4697 thread: TableId<GuestThread>,
4698 ) -> Result<QualifiedThreadId> {
4699 Ok(QualifiedThreadId {
4700 task: state.get_mut(thread)?.parent_task,
4701 thread,
4702 })
4703 }
4704}
4705
4706impl fmt::Debug for QualifiedThreadId {
4707 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4708 f.debug_tuple("QualifiedThreadId")
4709 .field(&self.task.rep())
4710 .field(&self.thread.rep())
4711 .finish()
4712 }
4713}
4714
4715enum GuestThreadState {
4716 NotStartedImplicit,
4717 NotStartedExplicit(
4718 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4719 ),
4720 Running,
4721 Suspended(StoreFiber<'static>),
4722 Ready {
4723 fiber: StoreFiber<'static>,
4724 cancellable: bool,
4725 },
4726 Completed,
4727}
4728pub struct GuestThread {
4729 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
4732 parent_task: TableId<GuestTask>,
4734 wake_on_cancel: Option<TableId<WaitableSet>>,
4737 state: GuestThreadState,
4739 instance_rep: Option<u32>,
4742 sync_call_set: TableId<WaitableSet>,
4744}
4745
4746impl GuestThread {
4747 fn from_instance(
4750 state: Pin<&mut ComponentInstance>,
4751 caller_instance: RuntimeComponentInstanceIndex,
4752 guest_thread: u32,
4753 ) -> Result<TableId<Self>> {
4754 let rep = state.instance_states().0[caller_instance]
4755 .thread_handle_table()
4756 .guest_thread_rep(guest_thread)?;
4757 Ok(TableId::new(rep))
4758 }
4759
4760 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
4761 let sync_call_set = state.push(WaitableSet {
4762 is_sync_call_set: true,
4763 ..WaitableSet::default()
4764 })?;
4765 Ok(Self {
4766 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
4767 parent_task,
4768 wake_on_cancel: None,
4769 state: GuestThreadState::NotStartedImplicit,
4770 instance_rep: None,
4771 sync_call_set,
4772 })
4773 }
4774
4775 fn new_explicit(
4776 state: &mut ConcurrentState,
4777 parent_task: TableId<GuestTask>,
4778 start_func: Box<
4779 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
4780 >,
4781 ) -> Result<Self> {
4782 let sync_call_set = state.push(WaitableSet {
4783 is_sync_call_set: true,
4784 ..WaitableSet::default()
4785 })?;
4786 Ok(Self {
4787 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
4788 parent_task,
4789 wake_on_cancel: None,
4790 state: GuestThreadState::NotStartedExplicit(start_func),
4791 instance_rep: None,
4792 sync_call_set,
4793 })
4794 }
4795}
4796
4797impl TableDebug for GuestThread {
4798 fn type_name() -> &'static str {
4799 "GuestThread"
4800 }
4801}
4802
4803enum SyncResult {
4804 NotProduced,
4805 Produced(Option<ValRaw>),
4806 Taken,
4807}
4808
4809impl SyncResult {
4810 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
4811 Ok(match mem::replace(self, SyncResult::Taken) {
4812 SyncResult::NotProduced => None,
4813 SyncResult::Produced(val) => Some(val),
4814 SyncResult::Taken => {
4815 bail_bug!("attempted to take a synchronous result that was already taken")
4816 }
4817 })
4818 }
4819}
4820
4821#[derive(Debug)]
4822enum HostFutureState {
4823 NotApplicable,
4824 Live,
4825 Dropped,
4826}
4827
4828pub(crate) struct GuestTask {
4830 common: WaitableCommon,
4832 lower_params: Option<RawLower>,
4834 lift_result: Option<LiftResult>,
4836 result: Option<LiftedResult>,
4839 callback: Option<CallbackFn>,
4842 caller: Caller,
4844 call_context: CallContext,
4849 sync_result: SyncResult,
4852 cancel_sent: bool,
4855 starting_sent: bool,
4858 instance: RuntimeInstance,
4865 event: Option<Event>,
4868 exited: bool,
4870 threads: HashSet<TableId<GuestThread>>,
4872 host_future_state: HostFutureState,
4875 async_function: bool,
4878
4879 decremented_interesting_task_count: bool,
4880}
4881
4882impl GuestTask {
4883 fn already_lowered_parameters(&self) -> bool {
4884 self.lower_params.is_none()
4886 }
4887
4888 fn returned_or_cancelled(&self) -> bool {
4889 self.lift_result.is_none()
4891 }
4892
4893 fn ready_to_delete(&self) -> bool {
4894 let threads_completed = self.threads.is_empty();
4895 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
4896 let pending_completion_event = matches!(
4897 self.common.event,
4898 Some(Event::Subtask {
4899 status: Status::Returned | Status::ReturnCancelled
4900 })
4901 );
4902 let ready = threads_completed
4903 && !has_sync_result
4904 && !pending_completion_event
4905 && !matches!(self.host_future_state, HostFutureState::Live);
4906 log::trace!(
4907 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
4908 threads_completed,
4909 has_sync_result,
4910 pending_completion_event,
4911 self.host_future_state
4912 );
4913 ready
4914 }
4915
4916 fn new(
4917 state: &mut ConcurrentState,
4918 lower_params: RawLower,
4919 lift_result: LiftResult,
4920 caller: Caller,
4921 callback: Option<CallbackFn>,
4922 instance: RuntimeInstance,
4923 async_function: bool,
4924 ) -> Result<QualifiedThreadId> {
4925 let host_future_state = match &caller {
4926 Caller::Guest { .. } => HostFutureState::NotApplicable,
4927 Caller::Host {
4928 host_future_present,
4929 ..
4930 } => {
4931 if *host_future_present {
4932 HostFutureState::Live
4933 } else {
4934 HostFutureState::NotApplicable
4935 }
4936 }
4937 };
4938 let task = state.push(Self {
4939 common: WaitableCommon::default(),
4940 lower_params: Some(lower_params),
4941 lift_result: Some(lift_result),
4942 result: None,
4943 callback,
4944 caller,
4945 call_context: CallContext::default(),
4946 sync_result: SyncResult::NotProduced,
4947 cancel_sent: false,
4948 starting_sent: false,
4949 instance,
4950 event: None,
4951 exited: false,
4952 threads: HashSet::new(),
4953 host_future_state,
4954 async_function,
4955 decremented_interesting_task_count: false,
4956 })?;
4957 let new_thread = GuestThread::new_implicit(state, task)?;
4958 let thread = state.push(new_thread)?;
4959 state.get_mut(task)?.threads.insert(thread);
4960 state.interesting_tasks += 1;
4961 Ok(QualifiedThreadId { task, thread })
4962 }
4963}
4964
4965impl TableDebug for GuestTask {
4966 fn type_name() -> &'static str {
4967 "GuestTask"
4968 }
4969}
4970
4971#[derive(Default)]
4973struct WaitableCommon {
4974 event: Option<Event>,
4976 set: Option<TableId<WaitableSet>>,
4978 handle: Option<u32>,
4980}
4981
4982#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4984enum Waitable {
4985 Host(TableId<HostTask>),
4987 Guest(TableId<GuestTask>),
4989 Transmit(TableId<TransmitHandle>),
4991}
4992
4993impl Waitable {
4994 fn from_instance(
4997 state: Pin<&mut ComponentInstance>,
4998 caller_instance: RuntimeComponentInstanceIndex,
4999 waitable: u32,
5000 ) -> Result<Self> {
5001 use crate::runtime::vm::component::Waitable;
5002
5003 let (waitable, kind) = state.instance_states().0[caller_instance]
5004 .handle_table()
5005 .waitable_rep(waitable)?;
5006
5007 Ok(match kind {
5008 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5009 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5010 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5011 })
5012 }
5013
5014 fn rep(&self) -> u32 {
5016 match self {
5017 Self::Host(id) => id.rep(),
5018 Self::Guest(id) => id.rep(),
5019 Self::Transmit(id) => id.rep(),
5020 }
5021 }
5022
5023 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5027 log::trace!("waitable {self:?} join set {set:?}");
5028
5029 let old = mem::replace(&mut self.common(state)?.set, set);
5030
5031 if let Some(old) = old {
5032 match *self {
5033 Waitable::Host(id) => state.remove_child(id, old),
5034 Waitable::Guest(id) => state.remove_child(id, old),
5035 Waitable::Transmit(id) => state.remove_child(id, old),
5036 }?;
5037
5038 state.get_mut(old)?.ready.remove(self);
5039 }
5040
5041 if let Some(set) = set {
5042 match *self {
5043 Waitable::Host(id) => state.add_child(id, set),
5044 Waitable::Guest(id) => state.add_child(id, set),
5045 Waitable::Transmit(id) => state.add_child(id, set),
5046 }?;
5047
5048 if self.common(state)?.event.is_some() {
5049 self.mark_ready(state)?;
5050 }
5051 }
5052
5053 Ok(())
5054 }
5055
5056 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5058 Ok(match self {
5059 Self::Host(id) => &mut state.get_mut(*id)?.common,
5060 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5061 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5062 })
5063 }
5064
5065 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5071 if self.common(state)?.set.is_some() {
5072 bail!(Trap::WaitableSyncAndAsync);
5073 }
5074 Ok(())
5075 }
5076
5077 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5081 log::trace!("set event for {self:?}: {event:?}");
5082 self.common(state)?.event = event;
5083 self.mark_ready(state)
5084 }
5085
5086 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5088 let common = self.common(state)?;
5089 let event = common.event.take();
5090 if let Some(set) = self.common(state)?.set {
5091 state.get_mut(set)?.ready.remove(self);
5092 }
5093
5094 Ok(event)
5095 }
5096
5097 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5101 if let Some(set) = self.common(state)?.set {
5102 state.get_mut(set)?.ready.insert(*self);
5103 if let Some((thread, mode)) = state.get_mut(set)?.waiting.pop_first() {
5104 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5105 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5106
5107 let item = match mode {
5108 WaitMode::Fiber(fiber) => WorkItem::ResumeFiber(fiber),
5109 WaitMode::Callback(instance) => WorkItem::GuestCall(
5110 state.get_mut(thread.task)?.instance.index,
5111 GuestCall {
5112 thread,
5113 kind: GuestCallKind::DeliverEvent {
5114 instance,
5115 set: Some(set),
5116 },
5117 },
5118 ),
5119 };
5120 state.push_high_priority(item);
5121 }
5122 }
5123 Ok(())
5124 }
5125
5126 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5128 match self {
5129 Self::Host(task) => {
5130 log::trace!("delete host task {task:?}");
5131 state.delete(*task)?;
5132 }
5133 Self::Guest(task) => {
5134 log::trace!("delete guest task {task:?}");
5135 let task = state.delete(*task)?;
5136
5137 debug_assert!(task.decremented_interesting_task_count);
5144 }
5145 Self::Transmit(task) => {
5146 state.delete(*task)?;
5147 }
5148 }
5149
5150 Ok(())
5151 }
5152}
5153
5154impl fmt::Debug for Waitable {
5155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5156 match self {
5157 Self::Host(id) => write!(f, "{id:?}"),
5158 Self::Guest(id) => write!(f, "{id:?}"),
5159 Self::Transmit(id) => write!(f, "{id:?}"),
5160 }
5161 }
5162}
5163
5164#[derive(Default)]
5166struct WaitableSet {
5167 ready: BTreeSet<Waitable>,
5169 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5171 is_sync_call_set: bool,
5174}
5175
5176impl TableDebug for WaitableSet {
5177 fn type_name() -> &'static str {
5178 "WaitableSet"
5179 }
5180}
5181
5182type RawLower =
5184 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5185
5186type RawLift = Box<
5188 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5189>;
5190
5191type LiftedResult = Box<dyn Any + Send + Sync>;
5195
5196struct DummyResult;
5199
5200#[derive(Default)]
5202pub struct ConcurrentInstanceState {
5203 backpressure: u16,
5205 do_not_enter: bool,
5207 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5210}
5211
5212impl ConcurrentInstanceState {
5213 pub fn pending_is_empty(&self) -> bool {
5214 self.pending.is_empty()
5215 }
5216}
5217
5218#[derive(Debug, Copy, Clone)]
5219pub(crate) enum CurrentThread {
5220 Guest(QualifiedThreadId),
5221 Host(TableId<HostTask>),
5222 None,
5223}
5224
5225impl CurrentThread {
5226 fn guest(&self) -> Option<&QualifiedThreadId> {
5227 match self {
5228 Self::Guest(id) => Some(id),
5229 _ => None,
5230 }
5231 }
5232
5233 fn host(&self) -> Option<TableId<HostTask>> {
5234 match self {
5235 Self::Host(id) => Some(*id),
5236 _ => None,
5237 }
5238 }
5239
5240 fn is_none(&self) -> bool {
5241 matches!(self, Self::None)
5242 }
5243}
5244
5245impl From<QualifiedThreadId> for CurrentThread {
5246 fn from(id: QualifiedThreadId) -> Self {
5247 Self::Guest(id)
5248 }
5249}
5250
5251impl From<TableId<HostTask>> for CurrentThread {
5252 fn from(id: TableId<HostTask>) -> Self {
5253 Self::Host(id)
5254 }
5255}
5256
5257pub struct ConcurrentState {
5259 unforced_current_thread: CurrentThread,
5265
5266 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5271 table: AlwaysMut<ResourceTable>,
5273 high_priority: Vec<WorkItem>,
5275 low_priority: VecDeque<WorkItem>,
5277 suspend_reason: Option<SuspendReason>,
5281 worker: Option<StoreFiber<'static>>,
5285 worker_item: Option<WorkerItem>,
5287
5288 global_error_context_ref_counts:
5301 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5302
5303 interesting_tasks: usize,
5316
5317 interesting_tasks_empty_waker: Option<Waker>,
5321
5322 ready_for_concurrent_call_waker: Option<Waker>,
5327}
5328
5329impl Default for ConcurrentState {
5330 fn default() -> Self {
5331 Self {
5332 unforced_current_thread: CurrentThread::None,
5333 table: AlwaysMut::new(ResourceTable::new()),
5334 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5335 high_priority: Vec::new(),
5336 low_priority: VecDeque::new(),
5337 suspend_reason: None,
5338 worker: None,
5339 worker_item: None,
5340 global_error_context_ref_counts: BTreeMap::new(),
5341 interesting_tasks: 0,
5342 interesting_tasks_empty_waker: None,
5343 ready_for_concurrent_call_waker: None,
5344 }
5345 }
5346}
5347
5348impl ConcurrentState {
5349 pub(crate) fn take_fibers_and_futures(
5366 &mut self,
5367 fibers: &mut Vec<StoreFiber<'static>>,
5368 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5369 ) {
5370 for entry in self.table.get_mut().iter_mut() {
5371 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5372 for mode in mem::take(&mut set.waiting).into_values() {
5373 if let WaitMode::Fiber(fiber) = mode {
5374 fibers.push(fiber);
5375 }
5376 }
5377 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5378 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5379 mem::replace(&mut thread.state, GuestThreadState::Completed)
5380 {
5381 fibers.push(fiber);
5382 }
5383 }
5384 }
5385
5386 if let Some(fiber) = self.worker.take() {
5387 fibers.push(fiber);
5388 }
5389
5390 let mut handle_item = |item| match item {
5391 WorkItem::ResumeFiber(fiber) => {
5392 fibers.push(fiber);
5393 }
5394 WorkItem::PushFuture(future) => {
5395 self.futures
5396 .get_mut()
5397 .as_mut()
5398 .unwrap()
5399 .push(future.into_inner());
5400 }
5401 WorkItem::ResumeThread(..) | WorkItem::GuestCall(..) | WorkItem::WorkerFunction(..) => {
5402 }
5403 };
5404
5405 for item in mem::take(&mut self.high_priority) {
5406 handle_item(item);
5407 }
5408 for item in mem::take(&mut self.low_priority) {
5409 handle_item(item);
5410 }
5411
5412 if let Some(them) = self.futures.get_mut().take() {
5413 futures.push(them);
5414 }
5415 }
5416
5417 #[cfg(feature = "gc")]
5418 pub(crate) fn trace_fiber_roots(
5419 &mut self,
5420 modules: &ModuleRegistry,
5421 unwind: &dyn Unwind,
5422 gc_roots_list: &mut GcRootsList,
5423 ) {
5424 let ConcurrentState {
5425 table,
5426 worker,
5427 high_priority,
5428 low_priority,
5429
5430 futures: _,
5434
5435 worker_item: _,
5437 unforced_current_thread: _,
5438 suspend_reason: _,
5439 global_error_context_ref_counts: _,
5440 interesting_tasks: _,
5441 interesting_tasks_empty_waker: _,
5442 ready_for_concurrent_call_waker: _,
5443 } = self;
5444
5445 for entry in table.get_mut().iter_mut() {
5446 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5447 for mode in set.waiting.values_mut() {
5448 if let WaitMode::Fiber(fiber) = mode {
5449 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5450 }
5451 }
5452 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5453 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5454 &mut thread.state
5455 {
5456 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5457 }
5458 }
5459 }
5460
5461 if let Some(fiber) = worker {
5462 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5463 }
5464
5465 let mut handle_item = |item: &mut WorkItem| match item {
5466 WorkItem::ResumeFiber(fiber) => {
5467 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5468 }
5469 WorkItem::PushFuture(_future) => {
5470 }
5473 WorkItem::ResumeThread(..) | WorkItem::GuestCall(..) | WorkItem::WorkerFunction(..) => {
5474 }
5475 };
5476
5477 for item in high_priority {
5478 handle_item(item);
5479 }
5480 for item in low_priority {
5481 handle_item(item);
5482 }
5483 }
5484
5485 fn push<V: Send + Sync + 'static>(
5486 &mut self,
5487 value: V,
5488 ) -> Result<TableId<V>, ResourceTableError> {
5489 self.table.get_mut().push(value).map(TableId::from)
5490 }
5491
5492 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5493 self.table.get_mut().get_mut(&Resource::from(id))
5494 }
5495
5496 pub fn add_child<T: 'static, U: 'static>(
5497 &mut self,
5498 child: TableId<T>,
5499 parent: TableId<U>,
5500 ) -> Result<(), ResourceTableError> {
5501 self.table
5502 .get_mut()
5503 .add_child(Resource::from(child), Resource::from(parent))
5504 }
5505
5506 pub fn remove_child<T: 'static, U: 'static>(
5507 &mut self,
5508 child: TableId<T>,
5509 parent: TableId<U>,
5510 ) -> Result<(), ResourceTableError> {
5511 self.table
5512 .get_mut()
5513 .remove_child(Resource::from(child), Resource::from(parent))
5514 }
5515
5516 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5517 self.table.get_mut().delete(Resource::from(id))
5518 }
5519
5520 fn push_future(&mut self, future: HostTaskFuture) {
5521 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5528 }
5529
5530 fn push_high_priority(&mut self, item: WorkItem) {
5531 log::trace!("push high priority: {item:?}");
5532 self.high_priority.push(item);
5533 }
5534
5535 fn push_low_priority(&mut self, item: WorkItem) {
5536 log::trace!("push low priority: {item:?}");
5537 self.low_priority.push_front(item);
5538 }
5539
5540 fn push_work_item(&mut self, item: WorkItem, high_priority: bool) {
5541 if high_priority {
5542 self.push_high_priority(item);
5543 } else {
5544 self.push_low_priority(item);
5545 }
5546 }
5547
5548 fn promote_instance_local_thread_work_item(
5549 &mut self,
5550 current_instance: RuntimeComponentInstanceIndex,
5551 ) -> bool {
5552 self.promote_work_items_matching(|item: &WorkItem| match item {
5553 WorkItem::ResumeThread(instance, _) | WorkItem::GuestCall(instance, _) => {
5554 *instance == current_instance
5555 }
5556 _ => false,
5557 })
5558 }
5559
5560 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> bool {
5561 self.promote_work_items_matching(|item: &WorkItem| match item {
5562 WorkItem::ResumeThread(_, t) | WorkItem::GuestCall(_, GuestCall { thread: t, .. }) => {
5563 *t == thread
5564 }
5565 _ => false,
5566 })
5567 }
5568
5569 fn promote_work_items_matching<F>(&mut self, mut predicate: F) -> bool
5570 where
5571 F: FnMut(&WorkItem) -> bool,
5572 {
5573 if self.high_priority.iter().any(&mut predicate) {
5577 true
5578 }
5579 else if let Some(idx) = self.low_priority.iter().position(&mut predicate) {
5582 let item = self.low_priority.remove(idx).unwrap();
5583 self.push_high_priority(item);
5584 true
5585 } else {
5586 false
5587 }
5588 }
5589
5590 fn check_blocking_for(&mut self, task: TableId<GuestTask>) -> Result<()> {
5591 if self.may_block(task)? {
5592 Ok(())
5593 } else {
5594 Err(Trap::CannotBlockSyncTask.into())
5595 }
5596 }
5597
5598 fn may_block(&mut self, task: TableId<GuestTask>) -> Result<bool> {
5599 let task = self.get_mut(task)?;
5600 Ok(task.async_function || task.returned_or_cancelled())
5601 }
5602
5603 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
5609 let (task, is_host) = (task >> 1, task & 1 == 1);
5610 if is_host {
5611 let task: TableId<HostTask> = TableId::new(task);
5612 Ok(&mut self.get_mut(task)?.call_context)
5613 } else {
5614 let task: TableId<GuestTask> = TableId::new(task);
5615 Ok(&mut self.get_mut(task)?.call_context)
5616 }
5617 }
5618
5619 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
5620 match self.futures.get_mut().as_mut() {
5621 Some(f) => Ok(f),
5622 None => bail_bug!("futures field of concurrent state is currently taken"),
5623 }
5624 }
5625
5626 pub(crate) fn table(&mut self) -> &mut ResourceTable {
5627 self.table.get_mut()
5628 }
5629
5630 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
5632 match cur {
5633 CurrentThread::Guest(thread) => {
5634 let task = self.get_mut(thread.task).ok()?;
5635 Some(match task.caller {
5636 Caller::Host { caller, .. } => caller,
5637 Caller::Guest { thread } => thread.into(),
5638 })
5639 }
5640 CurrentThread::Host(id) => Some(self.get_mut(id).ok()?.caller.into()),
5641 CurrentThread::None => None,
5642 }
5643 }
5644}
5645
5646fn for_any_lower<
5649 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
5650>(
5651 fun: F,
5652) -> F {
5653 fun
5654}
5655
5656fn for_any_lift<
5658 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5659>(
5660 fun: F,
5661) -> F {
5662 fun
5663}
5664
5665fn check_ambient_store(id: StoreId) {
5666 let message = "\
5667 `Future`s which depend on asynchronous component tasks, streams, or \
5668 futures to complete may only be polled from the event loop of the \
5669 store to which they belong. Please use \
5670 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
5671 ";
5672 tls::try_get(|store| {
5673 let matched = match store {
5674 tls::TryGet::Some(store) => store.id() == id,
5675 tls::TryGet::Taken | tls::TryGet::None => false,
5676 };
5677
5678 if !matched {
5679 panic!("{message}")
5680 }
5681 });
5682}
5683
5684fn check_recursive_run() {
5687 tls::try_get(|store| {
5688 if !matches!(store, tls::TryGet::None) {
5689 panic!("Recursive `StoreContextMut::run_concurrent` calls not supported")
5690 }
5691 });
5692}
5693
5694fn unpack_callback_code(code: u32) -> (u32, u32) {
5695 (code & 0xF, code >> 4)
5696}
5697
5698struct WaitableCheckParams {
5702 set: TableId<WaitableSet>,
5703 options: OptionsIndex,
5704 payload: u32,
5705}
5706
5707enum WaitableCheck {
5710 Wait,
5711 Poll,
5712}
5713
5714#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
5723pub struct GuestTaskId(TableId<GuestTask>);
5724
5725pub(crate) struct PreparedCall<R> {
5727 handle: Func,
5729 thread: QualifiedThreadId,
5731 param_count: usize,
5733 rx: oneshot::Receiver<LiftedResult>,
5736 runtime_instance: RuntimeInstance,
5738 _phantom: PhantomData<R>,
5739}
5740
5741impl<R> PreparedCall<R> {
5742 pub(crate) fn task_id(&self) -> TaskId {
5744 TaskId {
5745 task: self.thread.task,
5746 runtime_instance: self.runtime_instance,
5747 }
5748 }
5749}
5750
5751pub(crate) struct TaskId {
5753 task: TableId<GuestTask>,
5754 runtime_instance: RuntimeInstance,
5755}
5756
5757impl TaskId {
5758 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
5764 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
5765 let delete = if !task.already_lowered_parameters() {
5766 store.cancel_guest_subtask_without_lowered_parameters(
5767 self.runtime_instance,
5768 self.task,
5769 )?;
5770 true
5771 } else {
5772 task.host_future_state = HostFutureState::Dropped;
5773 task.ready_to_delete()
5774 };
5775 if delete {
5776 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
5777 }
5778 Ok(())
5779 }
5780}
5781
5782pub(crate) fn prepare_call<T, R>(
5788 mut store: StoreContextMut<T>,
5789 handle: Func,
5790 param_count: usize,
5791 host_future_present: bool,
5792 lower_params: impl FnOnce(Func, StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
5793 + Send
5794 + Sync
5795 + 'static,
5796 lift_result: impl FnOnce(Func, &mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
5797 + Send
5798 + Sync
5799 + 'static,
5800) -> Result<PreparedCall<R>> {
5801 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
5802
5803 let instance = handle.instance().id().get(store.0);
5804 let options = &instance.component().env_component().options[options];
5805 let ty = &instance.component().types()[ty];
5806 let async_function = ty.async_;
5807 let task_return_type = ty.results;
5808 let component_instance = raw_options.instance;
5809 let callback = options.callback.map(|i| instance.runtime_callback(i));
5810 let memory = options
5811 .memory()
5812 .map(|i| instance.runtime_memory(i))
5813 .map(SendSyncPtr::new);
5814 let string_encoding = options.string_encoding;
5815 let token = StoreToken::new(store.as_context_mut());
5816 let caller = store.0.current_thread()?;
5817 let state = store.0.concurrent_state_mut()?;
5818
5819 let (tx, rx) = oneshot::channel();
5820
5821 let instance = handle.instance().runtime_instance(component_instance);
5822 let thread = GuestTask::new(
5823 state,
5824 Box::new(for_any_lower(move |store, params| {
5825 lower_params(handle, token.as_context_mut(store), params)
5826 })),
5827 LiftResult {
5828 lift: Box::new(for_any_lift(move |store, result| {
5829 lift_result(handle, store, result)
5830 })),
5831 ty: task_return_type,
5832 memory,
5833 string_encoding,
5834 },
5835 Caller::Host {
5836 tx: Some(tx),
5837 host_future_present,
5838 caller,
5839 },
5840 callback.map(|callback| {
5841 let callback = SendSyncPtr::new(callback);
5842 let instance = handle.instance();
5843 Box::new(move |store: &mut dyn VMStore, event, handle| {
5844 let store = token.as_context_mut(store);
5845 unsafe { instance.call_callback(store, callback, event, handle) }
5848 }) as CallbackFn
5849 }),
5850 instance,
5851 async_function,
5852 )?;
5853
5854 if !store.0.may_enter(instance)? {
5855 bail!(Trap::CannotEnterComponent);
5856 }
5857
5858 Ok(PreparedCall {
5859 handle,
5860 thread,
5861 param_count,
5862 runtime_instance: instance,
5863 rx,
5864 _phantom: PhantomData,
5865 })
5866}
5867
5868pub(crate) struct QueuedCall<R> {
5869 store: StoreId,
5870 task: TableId<GuestTask>,
5871 rx: oneshot::Receiver<LiftedResult>,
5872 _marker: PhantomData<fn() -> R>,
5873}
5874
5875impl<R> QueuedCall<R> {
5876 pub(crate) fn new<T: 'static>(
5883 mut store: StoreContextMut<T>,
5884 prepared: PreparedCall<R>,
5885 ) -> Result<QueuedCall<R>> {
5886 let PreparedCall {
5887 handle,
5888 thread,
5889 param_count,
5890 rx,
5891 ..
5892 } = prepared;
5893
5894 queue_call0(store.as_context_mut(), handle, thread, param_count)?;
5895
5896 Ok(QueuedCall {
5897 store: store.0.id(),
5898 task: thread.task,
5899 rx,
5900 _marker: PhantomData,
5901 })
5902 }
5903
5904 fn task(&self) -> GuestTaskId {
5905 GuestTaskId(self.task)
5906 }
5907}
5908
5909impl<R> Future for QueuedCall<R>
5910where
5911 R: 'static,
5912{
5913 type Output = Result<R>;
5914
5915 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
5916 check_ambient_store(self.store);
5917 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
5918 Ok(r) => match r.downcast() {
5919 Ok(r) => Ok(*r),
5920 Err(_) => bail_bug!("wrong type of value produced"),
5921 },
5922 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
5923 })
5924 }
5925}
5926
5927fn queue_call0<T: 'static>(
5930 store: StoreContextMut<T>,
5931 handle: Func,
5932 guest_thread: QualifiedThreadId,
5933 param_count: usize,
5934) -> Result<()> {
5935 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
5936 let is_concurrent = raw_options.async_;
5937 let callback = raw_options.callback;
5938 let instance = handle.instance();
5939 let callee = handle.lifted_core_func(store.0);
5940 let post_return = handle.post_return_core_func(store.0);
5941 let callback = callback.map(|i| {
5942 let instance = instance.id().get(store.0);
5943 SendSyncPtr::new(instance.runtime_callback(i))
5944 });
5945
5946 log::trace!("queueing call {guest_thread:?}");
5947
5948 unsafe {
5952 instance.queue_call(
5953 store,
5954 guest_thread,
5955 SendSyncPtr::new(callee),
5956 param_count,
5957 1,
5958 is_concurrent,
5959 callback,
5960 post_return.map(SendSyncPtr::new),
5961 )
5962 }
5963}