1use self::error_contexts::GlobalErrorContextRefCount;
54use crate::component::func::{Func, call_post_return};
55use crate::component::{
56 HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
57};
58use crate::fiber::{self, StoreFiber, StoreFiberYield};
59use crate::hash_set::HashSet;
60#[cfg(feature = "gc")]
61use crate::module::ModuleRegistry;
62use crate::prelude::*;
63use crate::store::{Store, StoreId, StoreInner, StoreOpaque, StoreToken};
64#[cfg(feature = "gc")]
65use crate::vm::GcRootsList;
66use crate::vm::component::{CallContext, ComponentInstance, InstanceState};
67use crate::vm::{AlwaysMut, SendSyncPtr, VMFuncRef, VMLazyThread, VMMemoryDefinition, VMStore};
68use crate::{
69 AsContext, AsContextMut, FuncType, Result, StoreContext, StoreContextMut, ValRaw, ValType, bail,
70};
71use crate::{Instance as ModuleInstance, bail_bug};
72use alloc::borrow::ToOwned;
73use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
74use core::any::Any;
75use core::cell::UnsafeCell;
76use core::fmt;
77use core::future;
78use core::future::Future;
79use core::marker::PhantomData;
80use core::mem::{self, ManuallyDrop, MaybeUninit};
81use core::ops::DerefMut;
82use core::pin::{Pin, pin};
83use core::ptr::{self, NonNull};
84use core::task::{Context, Poll, Waker};
85use futures::channel::oneshot;
86use futures::stream::{FuturesUnordered, StreamExt};
87use futures_and_streams::{FlatAbi, ReturnCode, TransmitHandle, TransmitIndex};
88use table::{TableDebug, TableId};
89use wasmtime_environ::component::{
90 CanonicalAbiInfo, CanonicalOptions, CanonicalOptionsDataModel, MAX_FLAT_PARAMS,
91 MAX_FLAT_RESULTS, OptionsIndex, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
92 RuntimeComponentInstanceIndex, RuntimeTableIndex, StringEncoding,
93 TypeComponentGlobalErrorContextTableIndex, TypeComponentLocalErrorContextTableIndex,
94 TypeFuncIndex, TypeFutureTableIndex, TypeStreamTableIndex, TypeTupleIndex,
95};
96use wasmtime_environ::packed_option::ReservedValue;
97use wasmtime_environ::{NUM_COMPONENT_CONTEXT_SLOTS, Trap};
98#[cfg(feature = "gc")]
99use wasmtime_unwinder::Unwind;
100
101pub use abort::JoinHandle;
102pub use func::{FuncCallConcurrent, TypedFuncCallConcurrent};
103pub use future_stream_any::{FutureAny, StreamAny};
104pub use futures_and_streams::{
105 Destination, DirectDestination, DirectSource, ErrorContext, FutureConsumer, FutureProducer,
106 FutureReader, GuardedFutureReader, GuardedStreamReader, ReadBuffer, Source, StreamConsumer,
107 StreamProducer, StreamReader, StreamResult, VecBuffer, WriteBuffer,
108};
109pub(crate) use futures_and_streams::{ResourcePair, lower_error_context_to_index};
110
111mod abort;
112mod error_contexts;
113mod func;
114mod future_stream_any;
115mod futures_and_streams;
116pub(crate) mod table;
117pub(crate) mod tls;
118
119const BLOCKED: u32 = 0xffff_ffff;
122
123#[derive(Clone, Copy, Eq, PartialEq, Debug)]
125pub enum Status {
126 Starting = 0,
127 Started = 1,
128 Returned = 2,
129 StartCancelled = 3,
130 ReturnCancelled = 4,
131}
132
133impl Status {
134 pub fn pack(self, waitable: Option<u32>) -> u32 {
140 assert!(matches!(self, Status::Returned) == waitable.is_none());
141 let waitable = waitable.unwrap_or(0);
142 assert!(waitable < (1 << 28));
143 (waitable << 4) | (self as u32)
144 }
145}
146
147#[derive(Clone, Copy, Debug)]
150enum Event {
151 None,
152 Subtask {
153 status: Status,
154 },
155 StreamRead {
156 code: ReturnCode,
157 pending: Option<(TypeStreamTableIndex, u32)>,
158 },
159 StreamWrite {
160 code: ReturnCode,
161 pending: Option<(TypeStreamTableIndex, u32)>,
162 },
163 FutureRead {
164 code: ReturnCode,
165 pending: Option<(TypeFutureTableIndex, u32)>,
166 },
167 FutureWrite {
168 code: ReturnCode,
169 pending: Option<(TypeFutureTableIndex, u32)>,
170 },
171 Cancelled,
172}
173
174impl Event {
175 fn parts(self) -> (u32, u32) {
180 const EVENT_NONE: u32 = 0;
181 const EVENT_SUBTASK: u32 = 1;
182 const EVENT_STREAM_READ: u32 = 2;
183 const EVENT_STREAM_WRITE: u32 = 3;
184 const EVENT_FUTURE_READ: u32 = 4;
185 const EVENT_FUTURE_WRITE: u32 = 5;
186 const EVENT_CANCELLED: u32 = 6;
187 match self {
188 Event::None => (EVENT_NONE, 0),
189 Event::Cancelled => (EVENT_CANCELLED, 0),
190 Event::Subtask { status } => (EVENT_SUBTASK, status as u32),
191 Event::StreamRead { code, .. } => (EVENT_STREAM_READ, code.encode()),
192 Event::StreamWrite { code, .. } => (EVENT_STREAM_WRITE, code.encode()),
193 Event::FutureRead { code, .. } => (EVENT_FUTURE_READ, code.encode()),
194 Event::FutureWrite { code, .. } => (EVENT_FUTURE_WRITE, code.encode()),
195 }
196 }
197}
198
199mod callback_code {
201 pub const EXIT: u32 = 0;
202 pub const YIELD: u32 = 1;
203 pub const WAIT: u32 = 2;
204}
205
206const START_FLAG_ASYNC_CALLEE: u32 = wasmtime_environ::component::START_FLAG_ASYNC_CALLEE as u32;
210
211pub struct Access<'a, T: 'static, D: HasData + ?Sized = HasSelf<T>> {
217 store: StoreContextMut<'a, T>,
218 get_data: fn(&mut T) -> D::Data<'_>,
219}
220
221impl<'a, T, D> Access<'a, T, D>
222where
223 D: HasData + ?Sized,
224 T: 'static,
225{
226 pub fn new(store: StoreContextMut<'a, T>, get_data: fn(&mut T) -> D::Data<'_>) -> Self {
228 Self { store, get_data }
229 }
230
231 pub fn data_mut(&mut self) -> &mut T {
233 self.store.data_mut()
234 }
235
236 pub fn get(&mut self) -> D::Data<'_> {
238 (self.get_data)(self.data_mut())
239 }
240
241 pub fn spawn(&mut self, task: impl 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 Caller {
659 fiber: StoreFiber<'static>,
660 callee: TableId<GuestTask>,
661 },
662}
663
664#[derive(Debug)]
665enum WaitReason {
666 GuestSubtask(TableId<GuestTask>),
667 Other,
668}
669
670#[derive(Debug)]
672enum SuspendReason {
673 Waiting {
676 set: TableId<WaitableSet>,
677 thread: QualifiedThreadId,
678 },
679 WaitingForGuestSubtask {
680 caller: QualifiedThreadId,
681 callee: TableId<GuestTask>,
682 },
683 NeedWork,
686 Yielding { thread: QualifiedThreadId },
689 ExplicitlySuspending { thread: QualifiedThreadId },
691}
692
693enum GuestCallKind {
695 DeliverEvent {
698 instance: Instance,
700 set: Option<TableId<WaitableSet>>,
705 },
706 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
712 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
713}
714
715impl fmt::Debug for GuestCallKind {
716 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
717 match self {
718 Self::DeliverEvent { instance, set } => f
719 .debug_struct("DeliverEvent")
720 .field("instance", instance)
721 .field("set", set)
722 .finish(),
723 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
724 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
725 }
726 }
727}
728
729#[derive(Copy, Clone, Debug)]
731pub enum SuspensionTarget {
732 Resume(u32),
733 Promote(u32),
734 None,
735}
736
737#[derive(Copy, Clone, Debug)]
739pub enum ResumeThread {
740 Promote,
741 Resume,
742 ResumeLater,
743}
744
745#[derive(Debug)]
747struct GuestCall {
748 thread: QualifiedThreadId,
749 kind: GuestCallKind,
750}
751
752impl GuestCall {
753 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
763 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
764 let async_typed = task.async_typed;
765 let instance = task.instance;
766 let state = store.instance_state(instance).concurrent_state();
767
768 let ready = match &self.kind {
769 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
770 GuestCallKind::StartImplicit(_) => {
771 !async_typed || !(state.do_not_enter || state.backpressure > 0)
772 }
773 GuestCallKind::StartExplicit(_) => true,
774 };
775 log::trace!(
776 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
777 state.do_not_enter,
778 state.backpressure
779 );
780 Ok(ready)
781 }
782}
783
784enum WorkerItem {
786 GuestCall(GuestCall),
787 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
788}
789
790enum WorkItem {
793 PushFuture(AlwaysMut<HostTaskFuture>),
795 ResumeFiber {
797 instance: RuntimeInstance,
798 thread: QualifiedThreadId,
799 fiber: StoreFiber<'static>,
800 },
801 ResumeThread {
803 instance: RuntimeInstance,
804 thread: QualifiedThreadId,
805 },
806 GuestCall {
808 instance: RuntimeInstance,
809 call: GuestCall,
810 },
811 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
813}
814
815impl fmt::Debug for WorkItem {
816 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
817 match self {
818 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
819 Self::ResumeFiber {
820 instance, thread, ..
821 } => f
822 .debug_struct("ResumeFiber")
823 .field("instance", instance)
824 .field("thread", thread)
825 .finish(),
826 Self::ResumeThread { instance, thread } => f
827 .debug_struct("ResumeThread")
828 .field("instance", instance)
829 .field("thread", thread)
830 .finish(),
831 Self::GuestCall { instance, call } => f
832 .debug_struct("GuestCall")
833 .field("instance", instance)
834 .field("call", call)
835 .finish(),
836 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
837 }
838 }
839}
840
841#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
843pub(crate) enum WaitResult {
844 Cancelled,
845 Completed,
846}
847
848pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
856 store: &mut dyn VMStore,
857 host_task: EnteredHostTask,
858 future: impl Future<Output = Result<R>> + Send + 'static,
859) -> Result<R> {
860 let task = store.current_host_thread()?;
861
862 let mut future = Box::pin(async move {
866 let result = future.await?;
867 tls::get(move |store| {
868 let state = store.concurrent_state_mut()?;
869 let host_state = &mut state.get_mut(task)?.state;
870 assert!(matches!(host_state, HostTaskState::CalleeStarted));
871 *host_state = HostTaskState::CalleeFinished(Box::new(result));
872
873 Waitable::Host(task).set_event(
874 state,
875 Some(Event::Subtask {
876 status: Status::Returned,
877 }),
878 )?;
879
880 Ok(())
881 })
882 }) as HostTaskFuture;
883
884 let poll = tls::set(store, || {
888 future
889 .as_mut()
890 .poll(&mut Context::from_waker(&Waker::noop()))
891 });
892
893 let caller = match host_task {
894 Some(pair) => pair.1,
895 None => bail_bug!("host task wasn't created but should have been"),
896 };
897
898 match poll {
899 Poll::Ready(result) => result?,
901
902 Poll::Pending => {
907 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
908 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
909
910 let state = store.concurrent_state_mut()?;
911 state.push_future(future);
912
913 let set = state.get_mut(caller.thread)?.sync_call_set;
914 Waitable::Host(task).join(state, Some(set))?;
915
916 store.suspend(SuspendReason::Waiting {
917 set,
918 thread: caller,
919 })?;
920
921 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
925 }
926 }
927
928 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
930 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
931 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
932 Ok(result) => *result,
933 Err(_) => bail_bug!("host task finished with wrong type of result"),
934 }),
935 _ => bail_bug!("unexpected host task state after completion"),
936 }
937}
938
939fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
941 match call.kind {
942 GuestCallKind::DeliverEvent { instance, set } => {
943 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
944 Some(pair) => pair,
945 None => bail_bug!("delivering non-present event"),
946 };
947 let state = store.concurrent_state_mut()?;
948 let task = state.get_mut(call.thread.task)?;
949 let runtime_instance = task.instance;
950 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
951
952 log::trace!(
953 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
954 call.thread,
955 );
956
957 let old_thread = store.set_thread(call.thread)?;
958 log::trace!(
959 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
960 call.thread
961 );
962
963 store.enter_instance(runtime_instance);
964
965 let Some(callback) = store
966 .concurrent_state_mut()?
967 .get_mut(call.thread.task)?
968 .callback
969 .take()
970 else {
971 bail_bug!("guest task callback field not present")
972 };
973
974 let code = callback(store, event, handle)?;
975
976 store
977 .concurrent_state_mut()?
978 .get_mut(call.thread.task)?
979 .callback = Some(callback);
980
981 store.exit_instance(runtime_instance)?;
982
983 store.set_thread(old_thread)?;
984
985 instance.handle_callback_code(store, call.thread, runtime_instance.index, code)?;
986
987 log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
988 }
989 GuestCallKind::StartImplicit(fun) => {
990 fun(store)?;
991 }
992 GuestCallKind::StartExplicit(fun) => {
993 fun(store)?;
994 }
995 }
996
997 Ok(())
998}
999
1000impl<T> Store<T> {
1001 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1003 where
1004 T: Send + 'static,
1005 {
1006 ensure!(
1007 self.as_context().0.concurrency_support(),
1008 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1009 );
1010 self.as_context_mut().run_concurrent(fun).await
1011 }
1012
1013 #[doc(hidden)]
1014 pub fn assert_concurrent_state_empty(&mut self) {
1015 self.as_context_mut().assert_concurrent_state_empty();
1016 }
1017
1018 #[doc(hidden)]
1019 pub fn concurrent_state_table_size(&mut self) -> usize {
1020 self.as_context_mut().concurrent_state_table_size()
1021 }
1022
1023 pub fn spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> Result<JoinHandle>
1025 where
1026 T: 'static,
1027 {
1028 self.as_context_mut().spawn(task)
1029 }
1030}
1031
1032impl<T> StoreContextMut<'_, T> {
1033 #[doc(hidden)]
1044 pub fn assert_concurrent_state_empty(self) {
1045 let store = self.0;
1046 store
1047 .store_data_mut()
1048 .components
1049 .assert_instance_states_empty();
1050 let state = store.concurrent_state_mut().unwrap();
1051 assert!(
1052 state.table.get_mut().is_empty(),
1053 "non-empty table: {:?}",
1054 state.table.get_mut()
1055 );
1056 assert!(state.switch_item.is_none());
1057 assert!(state.high_priority.is_empty());
1058 assert!(state.low_priority.is_empty());
1059 assert!(state.unforced_current_thread.is_none());
1060 assert!(state.futures_mut().unwrap().is_empty());
1061 assert!(state.global_error_context_ref_counts.is_empty());
1062 }
1063
1064 #[doc(hidden)]
1069 pub fn concurrent_state_table_size(&mut self) -> usize {
1070 self.0
1071 .concurrent_state_mut()
1072 .unwrap()
1073 .table
1074 .get_mut()
1075 .iter_mut()
1076 .count()
1077 }
1078
1079 pub fn spawn(mut self, task: impl AccessorTask<T>) -> Result<JoinHandle>
1089 where
1090 T: 'static,
1091 {
1092 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1093 self.spawn_with_accessor(accessor, task)
1094 }
1095
1096 fn spawn_with_accessor<D>(
1099 self,
1100 accessor: Accessor<T, D>,
1101 task: impl AccessorTask<T, D>,
1102 ) -> Result<JoinHandle>
1103 where
1104 T: 'static,
1105 D: HasData + ?Sized,
1106 {
1107 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1111 self.0
1112 .concurrent_state_mut()?
1113 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1114 Ok(handle)
1115 }
1116
1117 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1201 where
1202 T: Send + 'static,
1203 {
1204 ensure!(
1205 self.0.concurrency_support(),
1206 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1207 );
1208 self.do_run_concurrent(fun, false).await
1209 }
1210
1211 pub(super) async fn run_concurrent_trap_on_idle<R>(
1212 self,
1213 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1214 ) -> Result<R> {
1215 self.do_run_concurrent(fun, true).await
1216 }
1217
1218 async fn do_run_concurrent<R>(
1219 mut self,
1220 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1221 trap_on_idle: bool,
1222 ) -> Result<R> {
1223 debug_assert!(self.0.concurrency_support());
1224 let already_running = self
1225 .0
1226 .concurrent_state_mut_already_forced_current_thread()
1227 .event_loop_running;
1228 if already_running {
1229 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1230 }
1231 let token = StoreToken::new(self.as_context_mut());
1232
1233 struct Dropper<'a, T: 'static, V> {
1234 store: StoreContextMut<'a, T>,
1235 value: ManuallyDrop<V>,
1236 }
1237
1238 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1239 fn drop(&mut self) {
1240 self.store
1241 .0
1242 .concurrent_state_mut_already_forced_current_thread()
1243 .event_loop_running = false;
1244
1245 tls::set(self.store.0, || {
1246 unsafe { ManuallyDrop::drop(&mut self.value) }
1251 });
1252 }
1253 }
1254
1255 let accessor = &Accessor::new(token);
1256 self.0
1257 .concurrent_state_mut_already_forced_current_thread()
1258 .event_loop_running = true;
1259 let dropper = &mut Dropper {
1260 store: self,
1261 value: ManuallyDrop::new(fun(accessor)),
1262 };
1263 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1265
1266 dropper
1267 .store
1268 .as_context_mut()
1269 .poll_until(future, trap_on_idle)
1270 .await
1271 }
1272
1273 async fn poll_until<R>(
1279 mut self,
1280 mut future: Pin<&mut impl Future<Output = R>>,
1281 trap_on_idle: bool,
1282 ) -> Result<R> {
1283 struct Reset<'a, T: 'static> {
1284 store: StoreContextMut<'a, T>,
1285 futures: Option<FuturesUnordered<HostTaskFuture>>,
1286 }
1287
1288 impl<'a, T> Drop for Reset<'a, T> {
1289 fn drop(&mut self) {
1290 if let Some(futures) = self.futures.take() {
1291 *self
1292 .store
1293 .0
1294 .concurrent_state_mut_already_forced_current_thread()
1295 .futures
1296 .get_mut() = Some(futures);
1297 }
1298 }
1299 }
1300
1301 loop {
1302 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1306 let mut reset = Reset {
1307 store: self.as_context_mut(),
1308 futures,
1309 };
1310 let mut next = match reset.futures.as_mut() {
1311 Some(f) => pin!(f.next()),
1312 None => bail_bug!("concurrent state missing futures field"),
1313 };
1314
1315 enum PollResult<R> {
1316 Complete(R),
1317 ProcessWork {
1318 ready: Option<WorkItem>,
1319 low_priority: bool,
1320 },
1321 }
1322
1323 let result = future::poll_fn(|cx| {
1324 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1327 return Poll::Ready(Ok(PollResult::Complete(value)));
1328 }
1329
1330 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1334 Poll::Ready(Some(output)) => {
1335 match output {
1336 Err(e) => return Poll::Ready(Err(e)),
1337 Ok(()) => {}
1338 }
1339 Poll::Ready(true)
1340 }
1341 Poll::Ready(None) => Poll::Ready(false),
1342 Poll::Pending => Poll::Pending,
1343 };
1344
1345 let state = reset.store.0.concurrent_state_mut()?;
1360 let mut ready = state.switch_item.take();
1361 let mut low_priority = false;
1362 if ready.is_none() {
1363 ready = state.high_priority.pop_back();
1364 if ready.is_none() {
1365 ready = state.low_priority.pop_back();
1366 low_priority = true;
1367 }
1368 }
1369 if ready.is_some() {
1370 return Poll::Ready(Ok(PollResult::ProcessWork {
1371 ready,
1372 low_priority,
1373 }));
1374 }
1375
1376 return match next {
1380 Poll::Ready(true) => {
1381 Poll::Ready(Ok(PollResult::ProcessWork {
1387 ready: None,
1388 low_priority: false,
1389 }))
1390 }
1391 Poll::Ready(false) => {
1392 if let Poll::Ready(value) =
1396 tls::set(reset.store.0, || future.as_mut().poll(cx))
1397 {
1398 Poll::Ready(Ok(PollResult::Complete(value)))
1399 } else {
1400 if trap_on_idle {
1406 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1413 Trap::CannotBlockSyncTask.into()
1414 } else {
1415 Trap::AsyncDeadlock.into()
1417 }))
1418 } else {
1419 Poll::Pending
1423 }
1424 }
1425 }
1426 Poll::Pending => Poll::Pending,
1431 };
1432 })
1433 .await;
1434
1435 drop(reset);
1439
1440 match result? {
1441 PollResult::Complete(value) => break Ok(value),
1444 PollResult::ProcessWork {
1447 ready,
1448 low_priority,
1449 } => {
1450 struct Dispose<'a, T: 'static> {
1451 store: StoreContextMut<'a, T>,
1452 ready: Option<WorkItem>,
1453 }
1454
1455 impl<'a, T> Drop for Dispose<'a, T> {
1456 fn drop(&mut self) {
1457 if let Some(item) = self.ready.take() {
1458 match item {
1459 WorkItem::ResumeFiber { mut fiber, .. } => {
1460 fiber.dispose(self.store.0)
1461 }
1462 WorkItem::PushFuture(future) => {
1463 tls::set(self.store.0, move || drop(future))
1464 }
1465 _ => {}
1466 }
1467 }
1468 }
1469 }
1470
1471 let mut dispose = Dispose {
1472 store: self.as_context_mut(),
1473 ready,
1474 };
1475
1476 if low_priority {
1498 dispose.store.0.yield_now().await
1499 }
1500
1501 if let Some(item) = dispose.ready.take() {
1502 dispose
1503 .store
1504 .as_context_mut()
1505 .handle_work_item(item)
1506 .await?;
1507 }
1508 }
1509 }
1510 }
1511 }
1512
1513 async fn handle_work_item(self, item: WorkItem) -> Result<()> {
1515 log::trace!("handle work item {item:?}");
1516 match item {
1517 WorkItem::PushFuture(future) => {
1518 self.0
1519 .concurrent_state_mut()?
1520 .futures_mut()?
1521 .push(future.into_inner());
1522 }
1523 WorkItem::ResumeFiber { fiber, .. } => {
1524 self.0.resume_fiber(fiber).await?;
1525 }
1526 WorkItem::ResumeThread { thread, .. } => {
1527 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1528 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1529 GuestThreadState::Running,
1530 ) {
1531 self.0.resume_fiber(fiber).await?;
1532 } else {
1533 bail_bug!("cannot resume non-pending thread {thread:?}");
1534 }
1535 }
1536 WorkItem::GuestCall { call, .. } => {
1537 if call.is_ready(self.0)? {
1538 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1539 } else {
1540 let state = self.0.concurrent_state_mut()?;
1541 let task = state.get_mut(call.thread.task)?;
1542 if !task.starting_sent {
1543 task.starting_sent = true;
1544 if let GuestCallKind::StartImplicit(_) = &call.kind {
1545 Waitable::Guest(call.thread.task).set_event(
1546 state,
1547 Some(Event::Subtask {
1548 status: Status::Starting,
1549 }),
1550 )?;
1551 }
1552 }
1553
1554 let instance = state.get_mut(call.thread.task)?.instance;
1555 self.0
1556 .instance_state(instance)
1557 .concurrent_state()
1558 .pending
1559 .insert(call.thread, call.kind);
1560 }
1561 }
1562 WorkItem::WorkerFunction(fun) => {
1563 self.run_on_worker(WorkerItem::Function(fun)).await?;
1564 }
1565 }
1566
1567 Ok(())
1568 }
1569
1570 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1572 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1573 fiber
1574 } else {
1575 unsafe {
1594 fiber::make_fiber_unchecked(self.0, move |store| {
1595 loop {
1596 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1597 bail_bug!("worker_item not present when resuming fiber")
1598 };
1599 match item {
1600 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1601 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1602 }
1603
1604 store.suspend(SuspendReason::NeedWork)?;
1605 }
1606 })?
1607 }
1608 };
1609
1610 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1611 assert!(worker_item.is_none());
1612 *worker_item = Some(item);
1613
1614 self.0.resume_fiber(worker).await
1615 }
1616
1617 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1622 where
1623 T: 'static,
1624 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1625 + Send
1626 + Sync
1627 + 'static,
1628 R: Send + Sync + 'static,
1629 {
1630 let token = StoreToken::new(self);
1631 async move {
1632 let mut accessor = Accessor::new(token);
1633 closure(&mut accessor).await
1634 }
1635 }
1636
1637 pub fn async_call_stack(&mut self) -> Result<impl Iterator<Item = GuestTaskId>> {
1659 let mut cur = Some(self.0.current_thread()?);
1660 let state = self.0.concurrent_state_mut()?;
1661 Ok(core::iter::from_fn(move || {
1662 while let Some(t) = cur {
1663 cur = state.parent(t);
1664 if let Some(task) = t.guest_task() {
1665 return Some(GuestTaskId(task));
1666 }
1667 }
1668
1669 None
1670 }))
1671 }
1672
1673 pub(crate) async fn start_instance(
1674 &mut self,
1675 instance: ModuleInstance,
1676 ) -> Result<ModuleInstance> {
1677 let (tx, rx) = oneshot::channel();
1678 let token = StoreToken::new(self.as_context_mut());
1679 self.0.queue_task(move |store| {
1680 _ = tx.send(
1681 instance
1682 .start_raw(&mut token.as_context_mut(store))
1683 .map(|()| instance),
1684 );
1685 Ok(())
1686 })?;
1687 self.as_context_mut()
1688 .run_concurrent_trap_on_idle(async |_| {
1689 rx.await
1690 .map_err(|_| format_err!("oneshot channel canceled"))
1691 })
1692 .await??
1693 }
1694}
1695
1696pub type EnteredHostTask = Option<(TableId<HostTask>, QualifiedThreadId)>;
1702
1703impl StoreOpaque {
1704 #[inline]
1707 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1708 if !self.concurrency_support() {
1710 return Ok(CurrentThread::None);
1711 }
1712
1713 if !self
1716 .vm_store_context_mut()
1717 .current_thread_mut()
1718 .is_deferred()
1719 {
1720 return Ok(self
1721 .concurrent_state_mut_already_forced_current_thread()
1722 .unforced_current_thread);
1723 }
1724
1725 self.force_deferred_current_thread()
1726 }
1727
1728 #[cold]
1731 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1732 let state = self.concurrent_state_mut_without_forcing_current_thread();
1741 let id = match state.unforced_current_thread.guest_task() {
1742 Some(task) => state.get_mut(task)?.instance.instance,
1743 None => bail_bug!("deferred component-model thread with non-guest base"),
1744 };
1745
1746 let mut frames = Vec::new();
1749 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1750 while let Some(ptr) = cur.as_deferred() {
1751 let deferred = unsafe { ptr.as_non_null().as_ref() };
1756 frames.push((
1757 deferred.callee_async != 0,
1758 deferred.callee_instance,
1759 deferred.saved_context,
1760 ));
1761 cur = deferred.parent;
1762 }
1763
1764 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1768
1769 let current_context = *self.vm_store_context_mut().component_context_mut();
1772
1773 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1777 *self.vm_store_context_mut().component_context_mut() = saved_context;
1781 let callee = RuntimeInstance {
1782 instance: id,
1783 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1784 };
1785 self.enter_guest_sync_call(callee_async, callee)?;
1786 }
1787
1788 *self.vm_store_context_mut().component_context_mut() = current_context;
1790
1791 Ok(self
1792 .concurrent_state_mut_without_forcing_current_thread()
1793 .unforced_current_thread)
1794 }
1795
1796 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1797 match self.current_thread()?.guest() {
1798 Some(id) => Ok(*id),
1799 None => bail_bug!("current thread is not a guest thread"),
1800 }
1801 }
1802
1803 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1804 match self.current_thread()?.host() {
1805 Some(id) => Ok(id),
1806 None => bail_bug!("current thread is not a host thread"),
1807 }
1808 }
1809
1810 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1811 log::trace!("enter sync-typed call {callee:?}");
1812 let state = self.instance_state(callee).concurrent_state();
1813 let old_do_not_suspend = state.do_not_suspend;
1814 state.do_not_suspend = true;
1815
1816 let thread = self.current_guest_thread()?;
1817 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1818 if thread.old_do_not_suspend.is_some() {
1819 bail_bug!("current thread already has `old_do_not_suspend` value");
1820 }
1821
1822 thread.old_do_not_suspend = Some(old_do_not_suspend);
1823
1824 Ok(())
1825 }
1826
1827 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1828 log::trace!("exit sync-typed call {callee:?}");
1829 let thread = self.current_guest_thread()?;
1830 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1831 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1832 bail_bug!("current thread missing `old_do_not_suspend` value");
1833 };
1834 let state = self.instance_state(callee).concurrent_state();
1835 state.do_not_suspend = old_do_not_suspend;
1836 Ok(())
1837 }
1838
1839 pub(crate) fn enter_guest_sync_call(
1851 &mut self,
1852 callee_async_typed: bool,
1853 callee: RuntimeInstance,
1854 ) -> Result<()> {
1855 log::trace!("enter sync-lifted call {callee:?}");
1856 if !self.concurrency_support() {
1857 return self.enter_call_not_concurrent();
1858 }
1859
1860 let thread = self.current_thread()?;
1861 let state = self.concurrent_state_mut()?;
1862 let guest_thread = GuestTask::new(
1863 state,
1864 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1865 LiftResult {
1866 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1867 ty: TypeTupleIndex::reserved_value(),
1868 memory: None,
1869 string_encoding: StringEncoding::Utf8,
1870 },
1871 if let Some(thread) = thread.guest() {
1872 Caller::Guest { thread: *thread }
1873 } else {
1874 Caller::Host {
1875 tx: None,
1876 host_future_present: false,
1877 caller: thread,
1878 }
1879 },
1880 None,
1881 callee,
1882 callee_async_typed,
1883 true,
1884 )?;
1885
1886 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1887 guest_thread.thread,
1888 self,
1889 callee.index,
1890 )?;
1891 self.set_thread(guest_thread)?;
1892
1893 if !callee_async_typed {
1894 self.enter_sync_call(callee)?;
1895 }
1896
1897 Ok(())
1898 }
1899
1900 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1908 if !self.concurrency_support() {
1909 return Ok(self.exit_call_not_concurrent());
1910 }
1911
1912 let thread = match self.current_thread()?.guest() {
1913 Some(t) => *t,
1914 None => bail_bug!("expected task when exiting"),
1915 };
1916 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1917 let instance = task.instance;
1918
1919 let caller = match &task.caller {
1920 &Caller::Guest { thread } => thread.into(),
1921 &Caller::Host { caller, .. } => caller,
1922 };
1923 task.lift_result = None;
1924 task.exited = true;
1925 let async_typed = task.async_typed;
1926
1927 if !async_typed {
1928 self.exit_sync_call(instance)?;
1929 }
1930
1931 self.set_thread(caller)?;
1932
1933 log::trace!("exit sync-lifted call {instance:?}");
1934
1935 if async_typed {
1936 self.switch_or_trap_if_may_not_suspend(instance)?;
1941 }
1942
1943 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1944
1945 Ok(())
1946 }
1947
1948 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
1956 if !self.concurrency_support() {
1957 self.enter_call_not_concurrent()?;
1958 return Ok(None);
1959 }
1960 let caller = self.current_guest_thread()?;
1961 let state = self.concurrent_state_mut()?;
1962 let task = state.push(HostTask::new(caller.task, HostTaskState::CalleeStarted))?;
1963 log::trace!("new host task {task:?}");
1964 self.set_thread(task)?;
1965 Ok(Some((task, caller)))
1966 }
1967
1968 pub(crate) fn host_task_delete(&mut self, task: EnteredHostTask) -> Result<()> {
1975 match task {
1976 Some((task, caller)) => {
1977 self.set_thread(caller)?;
1978 log::trace!("delete host task {task:?}");
1979 self.concurrent_state_mut()?.delete(task)?;
1980 }
1981 None => {
1982 self.exit_call_not_concurrent();
1983 }
1984 }
1985 Ok(())
1986 }
1987
1988 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
1991 self.component_instance_mut(instance.instance)
1992 .instance_state(instance.index)
1993 }
1994
1995 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2001 let thread = thread.into();
2002 let state = self.concurrent_state_mut()?;
2003 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2004
2005 if let Some(old_thread) = old_thread.guest() {
2013 let old_context = *self.vm_store_context_mut().component_context_mut();
2014 self.concurrent_state_mut()?
2015 .get_mut(old_thread.thread)?
2016 .context = old_context;
2017 }
2018 if cfg!(debug_assertions) {
2019 *self.vm_store_context_mut().component_context_mut() =
2020 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2021 }
2022 if let Some(thread) = thread.guest() {
2023 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2024 let context = thread.context;
2025 if cfg!(debug_assertions) {
2026 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2027 }
2028 *self.vm_store_context_mut().component_context_mut() = context;
2029 }
2030
2031 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2033 VMLazyThread::none()
2034 } else {
2035 VMLazyThread::forced()
2036 };
2037
2038 Ok(old_thread)
2039 }
2040
2041 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2043 if self.switch_if_may_not_suspend(instance)? {
2044 Ok(())
2045 } else {
2046 Err(Trap::CannotBlockSyncTask.into())
2047 }
2048 }
2049
2050 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2054 self.concurrent_state_mut()?;
2058
2059 Ok(!self.concurrency_support()
2060 || !self
2061 .instance_state(instance)
2062 .concurrent_state()
2063 .do_not_suspend
2064 || self
2065 .concurrent_state_mut()?
2066 .promote_instance_local_thread_work_item(instance)?)
2067 }
2068
2069 fn enter_instance(&mut self, instance: RuntimeInstance) {
2073 log::trace!("enter {instance:?}");
2074 self.instance_state(instance)
2075 .concurrent_state()
2076 .do_not_enter = true;
2077 }
2078
2079 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2083 log::trace!("exit {instance:?}");
2084 self.instance_state(instance)
2085 .concurrent_state()
2086 .do_not_enter = false;
2087 self.partition_pending(instance)
2088 }
2089
2090 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2098 for (thread, kind) in
2099 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2100 {
2101 let call = GuestCall { thread, kind };
2102 if call.is_ready(self)? {
2103 self.concurrent_state_mut()?
2104 .push_high_priority(WorkItem::GuestCall { instance, call });
2105 } else {
2106 self.instance_state(instance)
2107 .concurrent_state()
2108 .pending
2109 .insert(call.thread, call.kind);
2110 }
2111 }
2112
2113 if let Some(waker) = self
2114 .concurrent_state_mut()?
2115 .ready_for_concurrent_call_waker
2116 .take()
2117 {
2118 waker.wake();
2119 }
2120
2121 Ok(())
2122 }
2123
2124 pub(crate) fn backpressure_modify(
2126 &mut self,
2127 caller_instance: RuntimeInstance,
2128 modify: impl FnOnce(u16) -> Option<u16>,
2129 ) -> Result<()> {
2130 let state = self.instance_state(caller_instance).concurrent_state();
2131 let old = state.backpressure;
2132 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2133 state.backpressure = new;
2134
2135 if old > 0 && new == 0 {
2136 self.partition_pending(caller_instance)?;
2139 }
2140
2141 Ok(())
2142 }
2143
2144 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2147 let old_thread = self.current_thread()?;
2148 log::trace!("resume_fiber: save current thread {old_thread:?}");
2149
2150 let fiber = fiber::resolve_or_release(self, fiber).await?;
2151
2152 self.set_thread(old_thread)?;
2153
2154 let state = self.concurrent_state_mut()?;
2155
2156 if let Some(ot) = old_thread.guest() {
2157 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2158 }
2159 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2160
2161 if let Some(mut fiber) = fiber {
2162 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2163 let reason = match state.suspend_reason.take() {
2165 Some(r) => r,
2166 None => bail_bug!("suspend reason missing when resuming fiber"),
2167 };
2168 match reason {
2169 SuspendReason::NeedWork => {
2170 if state.worker.is_none() {
2171 state.worker = Some(fiber);
2172 } else {
2173 fiber.dispose(self);
2174 }
2175 }
2176 SuspendReason::Yielding { thread } => {
2177 state.get_mut(thread.thread)?.state = GuestThreadState::Ready { fiber };
2178 let instance = state.get_mut(thread.task)?.instance;
2179 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2180 }
2181 SuspendReason::ExplicitlySuspending { thread } => {
2182 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2183 }
2184 SuspendReason::Waiting { set, thread } => {
2185 let old = state
2186 .get_mut(set)?
2187 .waiting
2188 .insert(thread, WaitMode::Fiber(fiber));
2189 assert!(old.is_none());
2190 }
2191 SuspendReason::WaitingForGuestSubtask { caller, callee } => {
2192 let set = state.get_mut(caller.thread)?.sync_call_set;
2193 let old = state
2194 .get_mut(set)?
2195 .waiting
2196 .insert(caller, WaitMode::Caller { fiber, callee });
2197 assert!(old.is_none());
2198 }
2199 };
2200 } else {
2201 log::trace!("resume_fiber: fiber has exited");
2202 }
2203
2204 Ok(())
2205 }
2206
2207 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2213 log::trace!("suspend fiber: {reason:?}");
2214
2215 let task = match &reason {
2219 SuspendReason::Yielding { thread, .. }
2220 | SuspendReason::Waiting { thread, .. }
2221 | SuspendReason::WaitingForGuestSubtask { caller: thread, .. }
2222 | SuspendReason::ExplicitlySuspending { thread } => Some(thread.task),
2223 SuspendReason::NeedWork => None,
2224 };
2225
2226 let old_guest_thread = if let Some(task) = task {
2227 let state = self.concurrent_state_mut()?;
2233 if state.switch_item.is_none() {
2234 if let Some(item) = state.get_mut(task)?.switch_item.take() {
2235 state.set_switch_item(item)?;
2236 }
2237 }
2238
2239 self.current_thread()?
2240 } else {
2241 CurrentThread::None
2242 };
2243
2244 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2245 assert!(suspend_reason.is_none());
2246 *suspend_reason = Some(reason);
2247
2248 if !self.fiber_async_state_mut().can_block() {
2251 return Err(format_err!("future dropped"));
2252 }
2253
2254 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2255
2256 if task.is_some() {
2257 self.set_thread(old_guest_thread)?;
2258 }
2259
2260 Ok(())
2261 }
2262
2263 fn wait_for_event(
2264 &mut self,
2265 caller_instance: RuntimeInstance,
2266 waitable: Waitable,
2267 reason: WaitReason,
2268 ) -> Result<()> {
2269 let caller = self.current_guest_thread()?;
2270 let state = self.concurrent_state_mut()?;
2271
2272 waitable.trap_if_in_waitable_set(state)?;
2273
2274 let set = state.get_mut(caller.thread)?.sync_call_set;
2275 waitable.join(state, Some(set))?;
2276
2277 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2278
2279 self.suspend(match reason {
2280 WaitReason::GuestSubtask(callee) => {
2281 SuspendReason::WaitingForGuestSubtask { caller, callee }
2282 }
2283 WaitReason::Other => SuspendReason::Waiting {
2284 set,
2285 thread: caller,
2286 },
2287 })?;
2288 let state = self.concurrent_state_mut()?;
2289 waitable.join(state, None)
2290 }
2291
2292 fn cleanup_thread(
2314 &mut self,
2315 guest_thread: QualifiedThreadId,
2316 runtime_instance: RuntimeInstance,
2317 cleanup_task: CleanupTask,
2318 ) -> Result<()> {
2319 let state = self.concurrent_state_mut()?;
2320 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2323 state.set_switch_item(item)?;
2324 }
2325 let thread_data = state.get_mut(guest_thread.thread)?;
2326 let sync_call_set = thread_data.sync_call_set;
2327 if let Some(guest_id) = thread_data.instance_rep {
2328 self.instance_state(runtime_instance)
2329 .thread_handle_table()
2330 .guest_thread_remove(guest_id)?;
2331 }
2332 let state = self.concurrent_state_mut()?;
2333
2334 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2336 if let Some(Event::Subtask {
2337 status: Status::Returned | Status::ReturnCancelled,
2338 }) = waitable.common(state)?.event
2339 {
2340 waitable.delete_from(state)?;
2341 }
2342 }
2343
2344 state.delete(guest_thread.thread)?;
2345 state.delete(sync_call_set)?;
2346 let task = state.get_mut(guest_thread.task)?;
2347 task.threads.remove(&guest_thread.thread);
2348
2349 if task.threads.is_empty() && !task.returned_or_cancelled() {
2350 bail!(Trap::NoAsyncResult);
2351 }
2352 let ready_to_delete = task.ready_to_delete();
2353
2354 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2355 task.decremented_interesting_task_count = true;
2356
2357 debug_assert!(state.interesting_tasks > 0);
2358 state.interesting_tasks -= 1;
2359 if state.interesting_tasks == 0
2360 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2361 {
2362 waker.wake();
2363 }
2364 }
2365
2366 match cleanup_task {
2367 CleanupTask::Yes => {
2368 if ready_to_delete {
2369 Waitable::Guest(guest_thread.task).delete_from(state)?;
2370 }
2371 }
2372 CleanupTask::No => {}
2373 }
2374
2375 Ok(())
2376 }
2377
2378 fn cancel_guest_subtask_without_lowered_parameters(
2391 &mut self,
2392 caller_instance: RuntimeInstance,
2393 guest_task: TableId<GuestTask>,
2394 ) -> Result<()> {
2395 let concurrent_state = self.concurrent_state_mut()?;
2396 let task = concurrent_state.get_mut(guest_task)?;
2397 assert!(!task.already_lowered_parameters());
2398 task.lower_params = None;
2402 task.lift_result = None;
2403 task.exited = true;
2404 let instance = task.instance;
2405
2406 assert_eq!(1, task.threads.len());
2409 let thread = *task.threads.iter().next().unwrap();
2410 self.cleanup_thread(
2411 QualifiedThreadId {
2412 task: guest_task,
2413 thread,
2414 },
2415 caller_instance,
2416 CleanupTask::No,
2417 )?;
2418
2419 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2421 let pending_count = pending.len();
2422 pending.retain(|thread, _| thread.task != guest_task);
2423 if pending.len() == pending_count {
2425 bail!(Trap::SubtaskCancelAfterTerminal);
2426 }
2427 Ok(())
2428 }
2429
2430 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2433 if !self.concurrency_support() {
2434 return self.current_scope_id_not_concurrent();
2435 }
2436 let (bits, is_host) = match self.current_thread()? {
2437 CurrentThread::Guest(id) => (id.task.rep(), false),
2438 CurrentThread::GuestTask(id) => (id.rep(), false),
2439 CurrentThread::Host(id) => (id.rep(), true),
2440 CurrentThread::None => return Ok(None),
2441 };
2442 assert_eq!((bits << 1) >> 1, bits);
2443 Ok(Some((bits << 1) | u32::from(is_host)))
2444 }
2445
2446 fn queue_task(
2447 &mut self,
2448 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2449 ) -> Result<()> {
2450 self.concurrent_state_mut()?
2451 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2452 Ok(())
2453 }
2454
2455 fn any_may_not_suspend(&mut self) -> Result<bool> {
2464 Ok(self
2472 .concurrent_state_mut()?
2473 .table
2474 .get_mut()
2475 .iter_mut()
2476 .filter_map(|entry| {
2477 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2478 Some(task.instance)
2479 } else {
2480 None
2481 }
2482 })
2483 .collect::<Vec<_>>()
2484 .into_iter()
2485 .any(|instance| {
2486 self.instance_state(instance)
2487 .concurrent_state()
2488 .do_not_suspend
2489 }))
2490 }
2491}
2492
2493enum CleanupTask {
2494 Yes,
2495 No,
2496}
2497
2498impl Instance {
2499 fn get_event(
2502 self,
2503 store: &mut StoreOpaque,
2504 guest_task: TableId<GuestTask>,
2505 set: Option<TableId<WaitableSet>>,
2506 cancellable: bool,
2507 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2508 let state = store.concurrent_state_mut()?;
2509
2510 let event = &mut state.get_mut(guest_task)?.event;
2511 if let Some(ev) = event
2512 && (cancellable || !matches!(ev, Event::Cancelled))
2513 {
2514 log::trace!("deliver event {ev:?} to {guest_task:?}");
2515 let ev = *ev;
2516 *event = None;
2517 return Ok(Some((ev, None)));
2518 }
2519
2520 let set = match set {
2521 Some(set) => set,
2522 None => return Ok(None),
2523 };
2524 let waitable = match state.get_mut(set)?.ready.pop_first() {
2525 Some(v) => v,
2526 None => return Ok(None),
2527 };
2528
2529 let common = waitable.common(state)?;
2530 let handle = match common.handle {
2531 Some(h) => h,
2532 None => bail_bug!("handle not set when delivering event"),
2533 };
2534 let event = match common.event.take() {
2535 Some(e) => e,
2536 None => bail_bug!("event not set when delivering event"),
2537 };
2538
2539 log::trace!(
2540 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2541 );
2542
2543 waitable.on_delivery(store, self, event)?;
2544
2545 Ok(Some((event, Some((waitable, handle)))))
2546 }
2547
2548 fn handle_callback_code(
2554 self,
2555 store: &mut StoreOpaque,
2556 guest_thread: QualifiedThreadId,
2557 runtime_instance: RuntimeComponentInstanceIndex,
2558 code: u32,
2559 ) -> Result<()> {
2560 let (code, set) = unpack_callback_code(code);
2561
2562 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2563
2564 let state = store.concurrent_state_mut()?;
2565
2566 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2567 state.set_switch_item(item)?;
2568 }
2569
2570 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2571 let set = store
2572 .instance_state(self.runtime_instance(runtime_instance))
2573 .handle_table()
2574 .waitable_set_rep(handle)?;
2575
2576 Ok(TableId::<WaitableSet>::new(set))
2577 };
2578
2579 match code {
2580 callback_code::EXIT => {
2581 log::trace!("implicit thread {guest_thread:?} completed");
2582 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2583 task.exited = true;
2584 task.callback = None;
2585
2586 let runtime_instance = self.runtime_instance(runtime_instance);
2587
2588 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2593
2594 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2595 }
2596 callback_code::YIELD => {
2597 let task = state.get_mut(guest_thread.task)?;
2598 if let Some(event) = task.event {
2603 assert!(matches!(event, Event::None | Event::Cancelled));
2604 } else {
2605 task.event = Some(Event::None);
2606 }
2607 let call = GuestCall {
2608 thread: guest_thread,
2609 kind: GuestCallKind::DeliverEvent {
2610 instance: self,
2611 set: None,
2612 },
2613 };
2614 state.push_low_priority(WorkItem::GuestCall {
2617 instance: self.runtime_instance(runtime_instance),
2618 call,
2619 });
2620 }
2621 callback_code::WAIT => {
2622 let set = get_set(store, set)?;
2623 let state = store.concurrent_state_mut()?;
2624
2625 if state.get_mut(guest_thread.task)?.event.is_some()
2626 || !state.get_mut(set)?.ready.is_empty()
2627 {
2628 state.push_high_priority(WorkItem::GuestCall {
2630 instance: self.runtime_instance(runtime_instance),
2631 call: GuestCall {
2632 thread: guest_thread,
2633 kind: GuestCallKind::DeliverEvent {
2634 instance: self,
2635 set: Some(set),
2636 },
2637 },
2638 });
2639 } else {
2640 let old = state
2648 .get_mut(guest_thread.thread)?
2649 .wake_on_cancel
2650 .replace(set);
2651 if !old.is_none() {
2652 bail_bug!("thread unexpectedly had wake_on_cancel set");
2653 }
2654 let old = state
2655 .get_mut(set)?
2656 .waiting
2657 .insert(guest_thread, WaitMode::Callback(self));
2658 if !old.is_none() {
2659 bail_bug!("set's waiting set already had this thread registered");
2660 }
2661 }
2662 }
2663 _ => bail!(Trap::UnsupportedCallbackCode),
2664 }
2665
2666 Ok(())
2667 }
2668
2669 unsafe fn stage_call<T: 'static>(
2676 self,
2677 mut store: StoreContextMut<T>,
2678 guest_thread: QualifiedThreadId,
2679 callee: SendSyncPtr<VMFuncRef>,
2680 param_count: usize,
2681 result_count: usize,
2682 async_: bool,
2683 callback: Option<SendSyncPtr<VMFuncRef>>,
2684 post_return: Option<SendSyncPtr<VMFuncRef>>,
2685 host_caller: bool,
2686 ) -> Result<()> {
2687 unsafe fn make_call<T: 'static>(
2702 store: StoreContextMut<T>,
2703 guest_thread: QualifiedThreadId,
2704 callee: SendSyncPtr<VMFuncRef>,
2705 param_count: usize,
2706 result_count: usize,
2707 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2708 + Send
2709 + Sync
2710 + 'static
2711 + use<T> {
2712 let token = StoreToken::new(store);
2713 move |store: &mut dyn VMStore| {
2714 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2715
2716 store
2717 .concurrent_state_mut()?
2718 .get_mut(guest_thread.thread)?
2719 .state = GuestThreadState::Running;
2720 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2721 let lower = match task.lower_params.take() {
2722 Some(l) => l,
2723 None => bail_bug!("lower_params missing"),
2724 };
2725
2726 lower(store, &mut storage[..param_count])?;
2727
2728 let mut store = token.as_context_mut(store);
2729
2730 unsafe {
2733 crate::Func::call_unchecked_raw(
2734 &mut store,
2735 callee.as_non_null(),
2736 NonNull::new(
2737 &mut storage[..param_count.max(result_count)]
2738 as *mut [MaybeUninit<ValRaw>] as _,
2739 )
2740 .unwrap(),
2741 )?;
2742 }
2743
2744 Ok(storage)
2745 }
2746 }
2747
2748 let call = unsafe {
2752 make_call(
2753 store.as_context_mut(),
2754 guest_thread,
2755 callee,
2756 param_count,
2757 result_count,
2758 )
2759 };
2760
2761 let callee_instance = store
2762 .0
2763 .concurrent_state_mut()?
2764 .get_mut(guest_thread.task)?
2765 .instance;
2766
2767 let fun = if callback.is_some() {
2768 assert!(async_);
2769
2770 Box::new(move |store: &mut dyn VMStore| {
2771 self.add_guest_thread_to_instance_table(
2772 guest_thread.thread,
2773 store,
2774 callee_instance.index,
2775 )?;
2776 let old_thread = store.set_thread(guest_thread)?;
2777 log::trace!(
2778 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2779 );
2780
2781 store.enter_instance(callee_instance);
2782
2783 let storage = call(store)?;
2790
2791 store.exit_instance(callee_instance)?;
2792
2793 store.set_thread(old_thread)?;
2794 let state = store.concurrent_state_mut()?;
2795 if let Some(t) = old_thread.guest() {
2796 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2797 }
2798 log::trace!("stackless call: restored {old_thread:?} as current thread");
2799
2800 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2803
2804 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2805 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2806 } else {
2807 let token = StoreToken::new(store.as_context_mut());
2808 Box::new(move |store: &mut dyn VMStore| {
2809 self.add_guest_thread_to_instance_table(
2810 guest_thread.thread,
2811 store,
2812 callee_instance.index,
2813 )?;
2814 let old_thread = store.set_thread(guest_thread)?;
2815 log::trace!(
2816 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2817 );
2818 let flags = self.id().get(store).instance_flags(callee_instance.index);
2819
2820 let callee_async_typed = store
2821 .concurrent_state_mut()?
2822 .get_mut(guest_thread.task)?
2823 .async_typed;
2824
2825 if !async_ && callee_async_typed {
2829 store.enter_instance(callee_instance);
2830 }
2831
2832 if !callee_async_typed {
2833 store.enter_sync_call(callee_instance)?;
2834 }
2835
2836 let storage = call(store)?;
2843
2844 if !callee_async_typed {
2845 store.exit_sync_call(callee_instance)?;
2846 }
2847
2848 if !async_ {
2849 if callee_async_typed {
2855 store.exit_instance(callee_instance)?;
2856 }
2857
2858 let lift = {
2859 let state = store.concurrent_state_mut()?;
2860 if !state.get_mut(guest_thread.task)?.result.is_none() {
2861 bail_bug!("task has already produced a result");
2862 }
2863
2864 match state.get_mut(guest_thread.task)?.lift_result.take() {
2865 Some(lift) => lift,
2866 None => bail_bug!("lift_result field is missing"),
2867 }
2868 };
2869
2870 let result = (lift.lift)(store, unsafe {
2873 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2874 &storage[..result_count],
2875 )
2876 })?;
2877
2878 let post_return_arg = match result_count {
2879 0 => ValRaw::i32(0),
2880 1 => unsafe { storage[0].assume_init() },
2883 _ => unreachable!(),
2884 };
2885
2886 unsafe {
2887 call_post_return(
2888 token.as_context_mut(store),
2889 post_return.map(|v| v.as_non_null()),
2890 post_return_arg,
2891 flags,
2892 )?;
2893 }
2894
2895 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2896 }
2897
2898 store.set_thread(old_thread)?;
2899
2900 store
2901 .concurrent_state_mut()?
2902 .get_mut(guest_thread.task)?
2903 .exited = true;
2904
2905 log::trace!(
2906 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
2907 );
2908
2909 if callee_async_typed {
2910 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
2915 }
2916
2917 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2919 Ok(())
2920 })
2921 };
2922
2923 store.0.concurrent_state_mut()?.push_work_item(
2924 WorkItem::GuestCall {
2925 instance: callee_instance,
2926 call: GuestCall {
2927 thread: guest_thread,
2928 kind: GuestCallKind::StartImplicit(fun),
2929 },
2930 },
2931 if host_caller {
2932 Priority::High
2933 } else {
2934 Priority::Switch
2935 },
2936 )?;
2937
2938 Ok(())
2939 }
2940
2941 unsafe fn prepare_call<T: 'static>(
2954 self,
2955 mut store: StoreContextMut<T>,
2956 start: NonNull<VMFuncRef>,
2957 return_: NonNull<VMFuncRef>,
2958 caller_instance: RuntimeComponentInstanceIndex,
2959 callee_instance: RuntimeComponentInstanceIndex,
2960 task_return_type: TypeTupleIndex,
2961 callee_async_typed: bool,
2962 memory: *mut VMMemoryDefinition,
2963 string_encoding: StringEncoding,
2964 caller_info: CallerInfo,
2965 ) -> Result<()> {
2966 enum ResultInfo {
2967 Heap { results: u32 },
2968 Stack { result_count: u32 },
2969 }
2970
2971 let result_info = match &caller_info {
2972 CallerInfo::Async {
2973 has_result: true,
2974 params,
2975 } => ResultInfo::Heap {
2976 results: match params.last() {
2977 Some(r) => r.get_u32(),
2978 None => bail_bug!("retptr missing"),
2979 },
2980 },
2981 CallerInfo::Async {
2982 has_result: false, ..
2983 } => ResultInfo::Stack { result_count: 0 },
2984 CallerInfo::Sync {
2985 result_count,
2986 params,
2987 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
2988 results: match params.last() {
2989 Some(r) => r.get_u32(),
2990 None => bail_bug!("arg ptr missing"),
2991 },
2992 },
2993 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
2994 result_count: *result_count,
2995 },
2996 };
2997
2998 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
2999
3000 let start = SendSyncPtr::new(start);
3004 let return_ = SendSyncPtr::new(return_);
3005 let token = StoreToken::new(store.as_context_mut());
3006 let old_thread = store.0.current_guest_thread()?;
3007 let state = store.0.concurrent_state_mut()?;
3008
3009 debug_assert_eq!(
3010 state.get_mut(old_thread.task)?.instance,
3011 self.runtime_instance(caller_instance)
3012 );
3013
3014 let guest_thread = GuestTask::new(
3015 state,
3016 Box::new(move |store, dst| {
3017 let mut store = token.as_context_mut(store);
3018 assert!(dst.len() <= MAX_FLAT_PARAMS);
3019 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3021 let count = match caller_info {
3022 CallerInfo::Async { params, has_result } => {
3026 let params = ¶ms[..params.len() - usize::from(has_result)];
3027 for (param, src) in params.iter().zip(&mut src) {
3028 src.write(*param);
3029 }
3030 params.len()
3031 }
3032
3033 CallerInfo::Sync { params, .. } => {
3035 for (param, src) in params.iter().zip(&mut src) {
3036 src.write(*param);
3037 }
3038 params.len()
3039 }
3040 };
3041 unsafe {
3048 crate::Func::call_unchecked_raw(
3049 &mut store,
3050 start.as_non_null(),
3051 NonNull::new(
3052 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3053 )
3054 .unwrap(),
3055 )?;
3056 }
3057 dst.copy_from_slice(&src[..dst.len()]);
3058 let task = store.0.current_guest_thread()?.task;
3059 let state = store.0.concurrent_state_mut()?;
3060 Waitable::Guest(task).set_event(
3061 state,
3062 Some(Event::Subtask {
3063 status: Status::Started,
3064 }),
3065 )?;
3066 Ok(())
3067 }),
3068 LiftResult {
3069 lift: Box::new(move |store, src| {
3070 let mut store = token.as_context_mut(store);
3073 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3075 my_src.push(ValRaw::u32(*results));
3076 }
3077
3078 unsafe {
3085 crate::Func::call_unchecked_raw(
3086 &mut store,
3087 return_.as_non_null(),
3088 my_src.as_mut_slice().into(),
3089 )?;
3090 }
3091
3092 let thread = store.0.current_guest_thread()?;
3093 let state = store.0.concurrent_state_mut()?;
3094 if sync_caller {
3095 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3096 if let ResultInfo::Stack { result_count } = &result_info {
3097 match result_count {
3098 0 => None,
3099 1 => Some(my_src[0]),
3100 _ => unreachable!(),
3101 }
3102 } else {
3103 None
3104 },
3105 );
3106 }
3107 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3108 }),
3109 ty: task_return_type,
3110 memory: NonNull::new(memory).map(SendSyncPtr::new),
3111 string_encoding,
3112 },
3113 Caller::Guest { thread: old_thread },
3114 None,
3115 self.runtime_instance(callee_instance),
3116 callee_async_typed,
3117 false,
3120 )?;
3121
3122 store.0.set_thread(guest_thread)?;
3125 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3126
3127 Ok(())
3128 }
3129
3130 unsafe fn call_callback<T>(
3135 self,
3136 mut store: StoreContextMut<T>,
3137 function: SendSyncPtr<VMFuncRef>,
3138 event: Event,
3139 handle: u32,
3140 ) -> Result<u32> {
3141 let (ordinal, result) = event.parts();
3142 let params = &mut [
3143 ValRaw::u32(ordinal),
3144 ValRaw::u32(handle),
3145 ValRaw::u32(result),
3146 ];
3147 unsafe {
3152 crate::Func::call_unchecked_raw(
3153 &mut store,
3154 function.as_non_null(),
3155 params.as_mut_slice().into(),
3156 )?;
3157 }
3158 Ok(params[0].get_u32())
3159 }
3160
3161 unsafe fn start_call<T: 'static>(
3174 self,
3175 mut store: StoreContextMut<T>,
3176 callback: *mut VMFuncRef,
3177 post_return: *mut VMFuncRef,
3178 callee: NonNull<VMFuncRef>,
3179 param_count: u32,
3180 result_count: u32,
3181 flags: u32,
3182 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3183 ) -> Result<u32> {
3184 let token = StoreToken::new(store.as_context_mut());
3185 let async_caller = storage.is_none();
3186 let guest_thread = store.0.current_guest_thread()?;
3187 let state = store.0.concurrent_state_mut()?;
3188
3189 if !state.event_loop_running {
3190 bail_bug!("Instance::start_call called without a running event loop");
3191 }
3192
3193 let callee = SendSyncPtr::new(callee);
3194 let param_count = usize::try_from(param_count)?;
3195 assert!(param_count <= MAX_FLAT_PARAMS);
3196 let result_count = usize::try_from(result_count)?;
3197 assert!(result_count <= MAX_FLAT_RESULTS);
3198
3199 let task = state.get_mut(guest_thread.task)?;
3200 let callee_async_typed = task.async_typed;
3201 let callee_instance = task.instance;
3202
3203 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3204
3205 if let Some(callback) = NonNull::new(callback) {
3206 let callback = SendSyncPtr::new(callback);
3210 task.callback = Some(Box::new(move |store, event, handle| {
3211 let store = token.as_context_mut(store);
3212 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3213 }));
3214 }
3215
3216 let Caller::Guest { thread: caller } = &task.caller else {
3217 bail_bug!("start_call unexpectedly invoked for host->guest call");
3220 };
3221 let caller = *caller;
3222 let caller_instance = state.get_mut(caller.task)?.instance;
3223
3224 unsafe {
3226 self.stage_call(
3227 store.as_context_mut(),
3228 guest_thread,
3229 callee,
3230 param_count,
3231 result_count,
3232 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3233 NonNull::new(callback).map(SendSyncPtr::new),
3234 NonNull::new(post_return).map(SendSyncPtr::new),
3235 false,
3236 )?;
3237 }
3238
3239 let old_do_not_suspend = if callee_async_typed {
3240 let state = store.0.instance_state(callee_instance).concurrent_state();
3247 let old_do_not_suspend = state.do_not_suspend;
3248 state.do_not_suspend = false;
3249 Some(old_do_not_suspend)
3250 } else {
3251 None
3252 };
3253
3254 let state = store.0.concurrent_state_mut()?;
3255
3256 let guest_waitable = Waitable::Guest(guest_thread.task);
3259 let old_set = guest_waitable.common(state)?.set;
3260 let set = state.get_mut(caller.thread)?.sync_call_set;
3261 guest_waitable.join(state, Some(set))?;
3262
3263 store.0.set_thread(CurrentThread::None)?;
3264
3265 let (status, waitable) = loop {
3281 store.0.suspend(SuspendReason::WaitingForGuestSubtask {
3282 caller,
3283 callee: guest_thread.task,
3284 })?;
3285
3286 if let Some(old_do_not_suspend) = old_do_not_suspend {
3287 store
3288 .0
3289 .instance_state(callee_instance)
3290 .concurrent_state()
3291 .do_not_suspend = old_do_not_suspend;
3292 }
3293
3294 let state = store.0.concurrent_state_mut()?;
3295
3296 log::trace!("taking event for {:?}", guest_thread.task);
3297 let event = guest_waitable.take_event(state)?;
3298 let Some(Event::Subtask { status }) = event else {
3299 bail_bug!("subtasks should only get subtask events, got {event:?}")
3300 };
3301
3302 log::trace!("status {status:?} for {:?}", guest_thread.task);
3303
3304 if status == Status::Returned {
3305 break (status, None);
3307 } else if async_caller {
3308 let handle = store
3312 .0
3313 .instance_state(caller_instance)
3314 .handle_table()
3315 .subtask_insert_guest(guest_thread.task.rep())?;
3316 store
3317 .0
3318 .concurrent_state_mut()?
3319 .get_mut(guest_thread.task)?
3320 .common
3321 .handle = Some(handle);
3322 break (status, Some(handle));
3323 } else {
3324 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3328 }
3329 };
3330
3331 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3332
3333 store.0.set_thread(caller)?;
3335 store
3336 .0
3337 .concurrent_state_mut()?
3338 .get_mut(caller.thread)?
3339 .state = GuestThreadState::Running;
3340 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3341
3342 if let Some(storage) = storage {
3343 let state = store.0.concurrent_state_mut()?;
3347 let task = state.get_mut(guest_thread.task)?;
3348 if let Some(result) = task.sync_result.take()? {
3349 if let Some(result) = result {
3350 storage[0] = MaybeUninit::new(result);
3351 }
3352
3353 if task.exited && task.ready_to_delete() {
3354 Waitable::Guest(guest_thread.task).delete_from(state)?;
3355 }
3356 }
3357 }
3358
3359 Ok(status.pack(waitable))
3360 }
3361
3362 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3375 self,
3376 mut store: StoreContextMut<'_, T>,
3377 host_task: EnteredHostTask,
3378 future: impl Future<Output = Result<R>> + Send + 'static,
3379 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool) -> Result<()> + Send + 'static,
3380 ) -> Result<u32> {
3381 let token = StoreToken::new(store.as_context_mut());
3382 let task = store.0.current_host_thread()?;
3383 let state = store.0.concurrent_state_mut()?;
3384
3385 let (join_handle, future) = JoinHandle::run(future);
3388 {
3389 let state = &mut state.get_mut(task)?.state;
3390 assert!(matches!(state, HostTaskState::CalleeStarted));
3391 *state = HostTaskState::CalleeRunning(join_handle);
3392 }
3393
3394 let mut future = Box::pin(future);
3395
3396 let poll = tls::set(store.0, || {
3401 future
3402 .as_mut()
3403 .poll(&mut Context::from_waker(&Waker::noop()))
3404 });
3405
3406 match poll {
3407 Poll::Ready(result) => {
3409 let result = result.transpose()?;
3410 lower(store.as_context_mut(), result, true)?;
3411 return Ok(Status::Returned.pack(None));
3412 }
3413
3414 Poll::Pending => {}
3416 }
3417
3418 let future = Box::pin(async move {
3426 let result = match future.await {
3427 Some(result) => Some(result?),
3428 None => None,
3429 };
3430 let on_complete = move |store: &mut dyn VMStore| {
3431 let mut store = token.as_context_mut(store);
3435 let old = store.0.set_thread(task)?;
3436
3437 let status = if result.is_some() {
3438 Status::Returned
3439 } else {
3440 Status::ReturnCancelled
3441 };
3442
3443 lower(store.as_context_mut(), result, false)?;
3444 let state = store.0.concurrent_state_mut()?;
3445 match &mut state.get_mut(task)?.state {
3446 HostTaskState::CalleeDone { .. } => {}
3449
3450 other => *other = HostTaskState::CalleeDone { cancelled: false },
3452 }
3453 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3454
3455 store.0.set_thread(old)?;
3456 Ok(())
3457 };
3458
3459 tls::get(move |store| {
3464 store
3465 .concurrent_state_mut()?
3466 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3467 on_complete,
3468 ))));
3469 Ok(())
3470 })
3471 });
3472
3473 let caller = match host_task {
3476 Some(pair) => pair.1,
3477 None => bail_bug!("host task wasn't created but should have been"),
3478 };
3479 let state = store.0.concurrent_state_mut()?;
3480 state.push_future(future);
3481 let instance = state.get_mut(caller.task)?.instance;
3482 let handle = store
3483 .0
3484 .instance_state(instance)
3485 .handle_table()
3486 .subtask_insert_host(task.rep())?;
3487 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3488 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3489
3490 store.0.set_thread(caller)?;
3494 Ok(Status::Started.pack(Some(handle)))
3495 }
3496
3497 pub(crate) fn task_return(
3500 self,
3501 store: &mut dyn VMStore,
3502 ty: TypeTupleIndex,
3503 options: OptionsIndex,
3504 storage: &[ValRaw],
3505 ) -> Result<()> {
3506 let guest_thread = store.current_guest_thread()?;
3507 let state = store.concurrent_state_mut()?;
3508 let lift = state
3509 .get_mut(guest_thread.task)?
3510 .lift_result
3511 .take()
3512 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3513 if !state.get_mut(guest_thread.task)?.result.is_none() {
3514 bail_bug!("task result unexpectedly already set");
3515 }
3516
3517 let CanonicalOptions {
3518 string_encoding,
3519 data_model,
3520 ..
3521 } = &self.id().get(store).component().env_component().options[options];
3522
3523 let invalid = ty != lift.ty
3524 || string_encoding != &lift.string_encoding
3525 || match data_model {
3526 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3527 Some(memory) => {
3528 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3529 let actual = self.id().get(store).runtime_memory(memory);
3530 expected != actual.as_ptr()
3531 }
3532 None => false,
3535 },
3536 CanonicalOptionsDataModel::Gc { .. } => true,
3538 };
3539
3540 if invalid {
3541 bail!(Trap::TaskReturnInvalid);
3542 }
3543
3544 log::trace!("task.return for {guest_thread:?}");
3545
3546 let result = (lift.lift)(store, storage)?;
3547 self.task_complete(store, guest_thread.task, result, Status::Returned)
3548 }
3549
3550 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3552 let guest_thread = store.current_guest_thread()?;
3553 let state = store.concurrent_state_mut()?;
3554 let task = state.get_mut(guest_thread.task)?;
3555 if !task.cancel_sent {
3556 bail!(Trap::TaskCancelNotCancelled);
3557 }
3558 _ = task
3559 .lift_result
3560 .take()
3561 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3562
3563 if !task.result.is_none() {
3564 bail_bug!("task result should not bet set yet");
3565 }
3566
3567 log::trace!("task.cancel for {guest_thread:?}");
3568
3569 self.task_complete(
3570 store,
3571 guest_thread.task,
3572 Box::new(DummyResult),
3573 Status::ReturnCancelled,
3574 )
3575 }
3576
3577 fn task_complete(
3583 self,
3584 store: &mut StoreOpaque,
3585 guest_task: TableId<GuestTask>,
3586 result: Box<dyn Any + Send + Sync>,
3587 status: Status,
3588 ) -> Result<()> {
3589 store
3590 .component_resource_tables(Some(self))?
3591 .validate_scope_exit()?;
3592
3593 let state = store.concurrent_state_mut()?;
3594 let task = state.get_mut(guest_task)?;
3595
3596 if let Caller::Host { tx, .. } = &mut task.caller {
3597 if let Some(tx) = tx.take() {
3598 _ = tx.send(result);
3599 }
3600 } else {
3601 task.result = Some(result);
3602 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3603 }
3604
3605 Ok(())
3606 }
3607
3608 pub(crate) fn waitable_set_new(
3610 self,
3611 store: &mut StoreOpaque,
3612 caller_instance: RuntimeComponentInstanceIndex,
3613 ) -> Result<u32> {
3614 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3615 let handle = store
3616 .instance_state(self.runtime_instance(caller_instance))
3617 .handle_table()
3618 .waitable_set_insert(set.rep())?;
3619 log::trace!("new waitable set {set:?} (handle {handle})");
3620 Ok(handle)
3621 }
3622
3623 pub(crate) fn waitable_set_drop(
3625 self,
3626 store: &mut StoreOpaque,
3627 caller_instance: RuntimeComponentInstanceIndex,
3628 set: u32,
3629 ) -> Result<()> {
3630 let rep = store
3631 .instance_state(self.runtime_instance(caller_instance))
3632 .handle_table()
3633 .waitable_set_remove(set)?;
3634
3635 log::trace!("drop waitable set {rep} (handle {set})");
3636
3637 if !store
3641 .concurrent_state_mut()?
3642 .get_mut(TableId::<WaitableSet>::new(rep))?
3643 .waiting
3644 .is_empty()
3645 {
3646 bail!(Trap::WaitableSetDropHasWaiters);
3647 }
3648
3649 store
3650 .concurrent_state_mut()?
3651 .delete(TableId::<WaitableSet>::new(rep))?;
3652
3653 Ok(())
3654 }
3655
3656 pub(crate) fn waitable_join(
3658 self,
3659 store: &mut StoreOpaque,
3660 caller_instance: RuntimeComponentInstanceIndex,
3661 waitable_handle: u32,
3662 set_handle: u32,
3663 ) -> Result<()> {
3664 let mut instance = self.id().get_mut(store);
3665 let waitable =
3666 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3667
3668 let set = if set_handle == 0 {
3669 None
3670 } else {
3671 let set = instance.instance_states().0[caller_instance]
3672 .handle_table()
3673 .waitable_set_rep(set_handle)?;
3674
3675 let state = store.concurrent_state_mut()?;
3676 if let Some(old) = waitable.common(state)?.set
3677 && state.get_mut(old)?.is_sync_call_set
3678 {
3679 bail!(Trap::WaitableSyncAndAsync);
3680 }
3681
3682 Some(TableId::<WaitableSet>::new(set))
3683 };
3684
3685 log::trace!(
3686 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3687 );
3688
3689 waitable.join(store.concurrent_state_mut()?, set)
3690 }
3691
3692 pub(crate) fn subtask_drop(
3694 self,
3695 store: &mut StoreOpaque,
3696 caller_instance: RuntimeComponentInstanceIndex,
3697 task_id: u32,
3698 ) -> Result<()> {
3699 self.waitable_join(store, caller_instance, task_id, 0)?;
3700
3701 let (rep, is_host) = store
3702 .instance_state(self.runtime_instance(caller_instance))
3703 .handle_table()
3704 .subtask_remove(task_id)?;
3705
3706 let concurrent_state = store.concurrent_state_mut()?;
3707 let (waitable, delete) = if is_host {
3708 let id = TableId::<HostTask>::new(rep);
3709 let task = concurrent_state.get_mut(id)?;
3710 match &task.state {
3711 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3712 HostTaskState::CalleeDone { .. } => {}
3713 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3714 bail_bug!("invalid state for callee in `subtask.drop`")
3715 }
3716 }
3717 (Waitable::Host(id), true)
3718 } else {
3719 let id = TableId::<GuestTask>::new(rep);
3720 let task = concurrent_state.get_mut(id)?;
3721 if task.lift_result.is_some() {
3722 bail!(Trap::SubtaskDropNotResolved);
3723 }
3724 (
3725 Waitable::Guest(id),
3726 concurrent_state.get_mut(id)?.ready_to_delete(),
3727 )
3728 };
3729
3730 waitable.common(concurrent_state)?.handle = None;
3731
3732 if waitable.take_event(concurrent_state)?.is_some() {
3735 bail!(Trap::SubtaskDropNotResolved);
3736 }
3737
3738 if delete {
3739 waitable.delete_from(concurrent_state)?;
3740 }
3741
3742 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3743 Ok(())
3744 }
3745
3746 pub(crate) fn waitable_set_wait(
3748 self,
3749 store: &mut StoreOpaque,
3750 options: OptionsIndex,
3751 set: u32,
3752 payload: u32,
3753 ) -> Result<u32> {
3754 let &CanonicalOptions {
3755 instance: caller_instance,
3756 ..
3757 } = &self.id().get(store).component().env_component().options[options];
3758 let caller = self.runtime_instance(caller_instance);
3759 let rep = store
3760 .instance_state(self.runtime_instance(caller_instance))
3761 .handle_table()
3762 .waitable_set_rep(set)?;
3763
3764 self.waitable_check(
3765 store,
3766 caller,
3767 WaitableCheck::Wait,
3768 WaitableCheckParams {
3769 set: TableId::new(rep),
3770 options,
3771 payload,
3772 },
3773 )
3774 }
3775
3776 pub(crate) fn waitable_set_poll(
3778 self,
3779 store: &mut StoreOpaque,
3780 options: OptionsIndex,
3781 set: u32,
3782 payload: u32,
3783 ) -> Result<u32> {
3784 let &CanonicalOptions {
3785 instance: caller_instance,
3786 ..
3787 } = &self.id().get(store).component().env_component().options[options];
3788 let caller = self.runtime_instance(caller_instance);
3789 let rep = store
3790 .instance_state(caller)
3791 .handle_table()
3792 .waitable_set_rep(set)?;
3793
3794 self.waitable_check(
3795 store,
3796 caller,
3797 WaitableCheck::Poll,
3798 WaitableCheckParams {
3799 set: TableId::new(rep),
3800 options,
3801 payload,
3802 },
3803 )
3804 }
3805
3806 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3808 let thread_id = store.current_guest_thread()?.thread;
3809 match store
3810 .concurrent_state_mut()?
3811 .get_mut(thread_id)?
3812 .instance_rep
3813 {
3814 Some(r) => Ok(r),
3815 None => bail_bug!("thread should have instance_rep by now"),
3816 }
3817 }
3818
3819 pub(crate) fn thread_new_indirect<T: 'static>(
3821 self,
3822 mut store: StoreContextMut<T>,
3823 runtime_instance: RuntimeComponentInstanceIndex,
3824 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3826 start_func_idx: u32,
3827 context: i32,
3828 ) -> Result<u32> {
3829 log::trace!("creating new thread");
3830
3831 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3832 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3833 let callee = instance
3834 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3835 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3836 if callee.type_index(store.0) != start_func_ty.type_index() {
3837 bail!(Trap::ThreadNewIndirectInvalidType);
3838 }
3839
3840 let token = StoreToken::new(store.as_context_mut());
3841 let start_func = Box::new(
3842 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3843 let old_thread = store.set_thread(guest_thread)?;
3844 log::trace!(
3845 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3846 );
3847
3848 let mut store = token.as_context_mut(store);
3849 let mut params = [ValRaw::i32(context)];
3850 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3853
3854 store.0.set_thread(old_thread)?;
3855
3856 let runtime_instance = self.runtime_instance(runtime_instance);
3857
3858 store
3861 .0
3862 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
3863
3864 store
3865 .0
3866 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
3867
3868 log::trace!("explicit thread {guest_thread:?} completed");
3869 let state = store.0.concurrent_state_mut()?;
3870 if let Some(t) = old_thread.guest() {
3871 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3872 }
3873 log::trace!("thread start: restored {old_thread:?} as current thread");
3874
3875 Ok(())
3876 },
3877 );
3878
3879 let current_thread = store.0.current_guest_thread()?;
3880 let state = store.0.concurrent_state_mut()?;
3881 let parent_task = current_thread.task;
3882
3883 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3884 let thread_id = state.push(new_thread)?;
3885 state.get_mut(parent_task)?.threads.insert(thread_id);
3886
3887 log::trace!("new thread with id {thread_id:?} created");
3888
3889 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3890 }
3891
3892 pub(crate) fn resume_thread(
3893 self,
3894 store: &mut StoreOpaque,
3895 runtime_instance: RuntimeComponentInstanceIndex,
3896 thread_idx: u32,
3897 how: ResumeThread,
3898 ) -> Result<bool> {
3899 let thread_id =
3900 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3901 let state = store.concurrent_state_mut()?;
3902 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3903
3904 if store.current_guest_thread()? == guest_thread {
3905 bail!(Trap::CannotResumeThread);
3906 }
3907
3908 let state = store.concurrent_state_mut()?;
3909 let thread = state.get_mut(guest_thread.thread)?;
3910 let priority = match how {
3911 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
3912 ResumeThread::ResumeLater => Priority::Low,
3913 };
3914
3915 match (&how, &thread.state) {
3916 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
3918 (ResumeThread::Promote, _) => return Ok(false),
3919
3920 (
3923 ResumeThread::Resume | ResumeThread::ResumeLater,
3924 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
3925 ) => {}
3926 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
3927 bail!(Trap::CannotResumeThread)
3928 }
3929 }
3930
3931 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3932 GuestThreadState::NotStartedExplicit(start_func) => {
3933 log::trace!("starting thread {guest_thread:?}");
3934 let guest_call = WorkItem::GuestCall {
3935 instance: self.runtime_instance(runtime_instance),
3936 call: GuestCall {
3937 thread: guest_thread,
3938 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3939 start_func(store, guest_thread)
3940 })),
3941 },
3942 };
3943 store
3944 .concurrent_state_mut()?
3945 .push_work_item(guest_call, priority)?;
3946 }
3947 GuestThreadState::Suspended(fiber) => {
3948 log::trace!("resuming thread {thread_id:?} that was suspended");
3949 store.concurrent_state_mut()?.push_work_item(
3950 WorkItem::ResumeFiber {
3951 instance: self.runtime_instance(runtime_instance),
3952 thread: guest_thread,
3953 fiber,
3954 },
3955 priority,
3956 )?;
3957 }
3958 GuestThreadState::Ready { fiber } => {
3959 log::trace!("resuming thread {thread_id:?} that was ready");
3960 thread.state = GuestThreadState::Ready { fiber };
3961 store
3962 .concurrent_state_mut()?
3963 .promote_thread_work_item(guest_thread)?;
3964 }
3965 other @ (GuestThreadState::NotStartedImplicit
3966 | GuestThreadState::Running
3967 | GuestThreadState::Completed) => {
3968 thread.state = other;
3969 }
3970 }
3971 Ok(true)
3972 }
3973
3974 fn add_guest_thread_to_instance_table(
3975 self,
3976 thread_id: TableId<GuestThread>,
3977 store: &mut StoreOpaque,
3978 runtime_instance: RuntimeComponentInstanceIndex,
3979 ) -> Result<u32> {
3980 let guest_id = store
3981 .instance_state(self.runtime_instance(runtime_instance))
3982 .thread_handle_table()
3983 .guest_thread_insert(thread_id.rep())?;
3984 store
3985 .concurrent_state_mut()?
3986 .get_mut(thread_id)?
3987 .instance_rep = Some(guest_id);
3988 Ok(guest_id)
3989 }
3990
3991 pub(crate) fn suspension_intrinsic(
3995 self,
3996 store: &mut StoreOpaque,
3997 caller: RuntimeComponentInstanceIndex,
3998 yielding: bool,
3999 to_thread: SuspensionTarget,
4000 ) -> Result<WaitResult> {
4001 let check_suspend = match to_thread {
4002 SuspensionTarget::Promote(thread) => {
4003 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4004 }
4005 SuspensionTarget::Resume(thread) => {
4006 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4007 bail_bug!(
4008 "`resume_thread` should only ever return false \
4009 when `ResumeThread::Promote` is passed to it"
4010 );
4011 }
4012 false
4013 }
4014 SuspensionTarget::None => true,
4015 };
4016
4017 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4018 return if yielding {
4019 Ok(WaitResult::Completed)
4020 } else {
4021 Err(Trap::CannotBlockSyncTask.into())
4022 };
4023 }
4024
4025 let guest_thread = store.current_guest_thread()?;
4026
4027 let reason = if yielding {
4028 SuspendReason::Yielding {
4029 thread: guest_thread,
4030 }
4031 } else {
4032 SuspendReason::ExplicitlySuspending {
4033 thread: guest_thread,
4034 }
4035 };
4036
4037 store.suspend(reason)?;
4038
4039 Ok(WaitResult::Completed)
4040 }
4041
4042 fn waitable_check(
4044 self,
4045 store: &mut StoreOpaque,
4046 caller: RuntimeInstance,
4047 check: WaitableCheck,
4048 params: WaitableCheckParams,
4049 ) -> Result<u32> {
4050 let guest_thread = store.current_guest_thread()?;
4051
4052 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4053
4054 let state = store.concurrent_state_mut()?;
4055 let task = state.get_mut(guest_thread.task)?;
4056
4057 match &check {
4060 WaitableCheck::Wait => {
4061 let set = params.set;
4062
4063 if (task.event.is_none() || matches!(task.event, Some(Event::Cancelled)))
4064 && state.get_mut(set)?.ready.is_empty()
4065 {
4066 store.switch_or_trap_if_may_not_suspend(caller)?;
4067
4068 store.suspend(SuspendReason::Waiting {
4069 set,
4070 thread: guest_thread,
4071 })?;
4072 }
4073 }
4074 WaitableCheck::Poll => {}
4075 }
4076
4077 log::trace!(
4078 "waitable check for {guest_thread:?}; set {:?}, part two",
4079 params.set
4080 );
4081
4082 let event = self.get_event(store, guest_thread.task, Some(params.set), false)?;
4084
4085 let (ordinal, handle, result) = match &check {
4086 WaitableCheck::Wait => {
4087 let (event, waitable) = match event {
4088 Some(p) => p,
4089 None => bail_bug!("event expected to be present"),
4090 };
4091 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4092 let (ordinal, result) = event.parts();
4093 (ordinal, handle, result)
4094 }
4095 WaitableCheck::Poll => {
4096 if let Some((event, waitable)) = event {
4097 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4098 let (ordinal, result) = event.parts();
4099 (ordinal, handle, result)
4100 } else {
4101 log::trace!(
4102 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4103 guest_thread.task,
4104 params.set
4105 );
4106 let (ordinal, result) = Event::None.parts();
4107 (ordinal, 0, result)
4108 }
4109 }
4110 };
4111 let memory = self.options_memory_mut(store, params.options);
4112 let ptr = crate::component::func::validate_inbounds_dynamic(
4113 &CanonicalAbiInfo::POINTER_PAIR,
4114 memory,
4115 &ValRaw::u32(params.payload),
4116 )?;
4117 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4118 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4119 Ok(ordinal)
4120 }
4121
4122 pub(crate) fn subtask_cancel(
4124 self,
4125 store: &mut StoreOpaque,
4126 caller_instance: RuntimeComponentInstanceIndex,
4127 async_: bool,
4128 task_id: u32,
4129 ) -> Result<u32> {
4130 let (rep, is_host) = store
4131 .instance_state(self.runtime_instance(caller_instance))
4132 .handle_table()
4133 .subtask_rep(task_id)?;
4134 let waitable = if is_host {
4135 Waitable::Host(TableId::<HostTask>::new(rep))
4136 } else {
4137 Waitable::Guest(TableId::<GuestTask>::new(rep))
4138 };
4139 let concurrent_state = store.concurrent_state_mut()?;
4140
4141 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4142
4143 waitable.trap_if_in_waitable_set(concurrent_state)?;
4144
4145 let needs_block;
4146 if let Waitable::Host(host_task) = waitable {
4147 let state = &mut concurrent_state.get_mut(host_task)?.state;
4148 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
4149 HostTaskState::CalleeRunning(handle) => {
4156 handle.abort();
4157 needs_block = true;
4158 }
4159
4160 HostTaskState::CalleeDone { cancelled } => {
4163 if cancelled {
4164 bail!(Trap::SubtaskCancelAfterTerminal);
4165 } else {
4166 needs_block = false;
4169 }
4170 }
4171
4172 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4175 bail_bug!("invalid states for host callee")
4176 }
4177 }
4178 } else {
4179 let guest_task = TableId::<GuestTask>::new(rep);
4180 let task = concurrent_state.get_mut(guest_task)?;
4181 if !task.already_lowered_parameters() {
4182 store.cancel_guest_subtask_without_lowered_parameters(
4183 self.runtime_instance(caller_instance),
4184 guest_task,
4185 )?;
4186 return Ok(Status::StartCancelled as u32);
4187 } else if !task.returned_or_cancelled() {
4188 task.cancel_sent = true;
4191 task.event = Some(Event::Cancelled);
4196 let runtime_instance = task.instance;
4197 for thread in task.threads.clone() {
4198 let thread = QualifiedThreadId {
4199 task: guest_task,
4200 thread,
4201 };
4202 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4203
4204 let yield_ = |store: &mut StoreOpaque| {
4205 let state = store.instance_state(runtime_instance).concurrent_state();
4210 let old_do_not_suspend = state.do_not_suspend;
4211 state.do_not_suspend = false;
4212
4213 let caller = store.current_guest_thread()?;
4214
4215 let state = store.concurrent_state_mut()?;
4220 let set = state.get_mut(caller.thread)?.sync_call_set;
4221 waitable.join(state, Some(set))?;
4222
4223 store.suspend(SuspendReason::Yielding { thread: caller })?;
4224
4225 let state = store.concurrent_state_mut()?;
4226 waitable.join(state, None)?;
4227
4228 store
4229 .instance_state(runtime_instance)
4230 .concurrent_state()
4231 .do_not_suspend = old_do_not_suspend;
4232
4233 Ok::<(), crate::Error>(())
4234 };
4235
4236 if let Some(set) = thread_mut.wake_on_cancel.take() {
4237 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4239 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber {
4240 instance: runtime_instance,
4241 thread,
4242 fiber,
4243 },
4244 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4245 instance: runtime_instance,
4246 call: GuestCall {
4247 thread,
4248 kind: GuestCallKind::DeliverEvent {
4249 instance,
4250 set: None,
4251 },
4252 },
4253 },
4254 Some(WaitMode::Caller { .. }) => {
4255 bail_bug!("unexpected `WaitMode::Caller` in wake_on_cancel set")
4256 }
4257 None => bail_bug!("thread not present in wake_on_cancel set"),
4258 };
4259 concurrent_state.set_switch_item(item)?;
4260
4261 yield_(store)?;
4262
4263 break;
4264 }
4265 }
4266
4267 needs_block = !store
4270 .concurrent_state_mut()?
4271 .get_mut(guest_task)?
4272 .returned_or_cancelled()
4273 } else {
4274 needs_block = false;
4275 }
4276 };
4277
4278 if needs_block {
4282 if async_ {
4283 return Ok(BLOCKED);
4284 }
4285
4286 store.wait_for_event(
4289 self.runtime_instance(caller_instance),
4290 waitable,
4291 if is_host {
4292 WaitReason::Other
4293 } else {
4294 WaitReason::GuestSubtask(TableId::<GuestTask>::new(rep))
4295 },
4296 )?;
4297
4298 }
4300
4301 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4302 if let Some(Event::Subtask {
4303 status: status @ (Status::Returned | Status::ReturnCancelled),
4304 }) = event
4305 {
4306 Ok(status as u32)
4307 } else {
4308 bail!(Trap::SubtaskCancelAfterTerminal);
4309 }
4310 }
4311}
4312
4313pub trait VMComponentAsyncStore {
4321 unsafe fn prepare_call(
4327 &mut self,
4328 instance: Instance,
4329 memory: *mut VMMemoryDefinition,
4330 start: NonNull<VMFuncRef>,
4331 return_: NonNull<VMFuncRef>,
4332 caller_instance: RuntimeComponentInstanceIndex,
4333 callee_instance: RuntimeComponentInstanceIndex,
4334 task_return_type: TypeTupleIndex,
4335 callee_async: bool,
4336 string_encoding: StringEncoding,
4337 result_count: u32,
4338 storage: *mut ValRaw,
4339 storage_len: usize,
4340 ) -> Result<()>;
4341
4342 unsafe fn sync_start(
4345 &mut self,
4346 instance: Instance,
4347 callback: *mut VMFuncRef,
4348 callee: NonNull<VMFuncRef>,
4349 param_count: u32,
4350 storage: *mut MaybeUninit<ValRaw>,
4351 storage_len: usize,
4352 ) -> Result<()>;
4353
4354 unsafe fn async_start(
4357 &mut self,
4358 instance: Instance,
4359 callback: *mut VMFuncRef,
4360 post_return: *mut VMFuncRef,
4361 callee: NonNull<VMFuncRef>,
4362 param_count: u32,
4363 result_count: u32,
4364 flags: u32,
4365 ) -> Result<u32>;
4366
4367 fn future_write(
4369 &mut self,
4370 instance: Instance,
4371 caller: RuntimeComponentInstanceIndex,
4372 ty: TypeFutureTableIndex,
4373 options: OptionsIndex,
4374 future: u32,
4375 address: u32,
4376 ) -> Result<u32>;
4377
4378 fn future_read(
4380 &mut self,
4381 instance: Instance,
4382 caller: RuntimeComponentInstanceIndex,
4383 ty: TypeFutureTableIndex,
4384 options: OptionsIndex,
4385 future: u32,
4386 address: u32,
4387 ) -> Result<u32>;
4388
4389 fn future_drop_writable(
4391 &mut self,
4392 instance: Instance,
4393 ty: TypeFutureTableIndex,
4394 writer: u32,
4395 ) -> Result<()>;
4396
4397 fn stream_write(
4399 &mut self,
4400 instance: Instance,
4401 caller: RuntimeComponentInstanceIndex,
4402 ty: TypeStreamTableIndex,
4403 options: OptionsIndex,
4404 stream: u32,
4405 address: u32,
4406 count: u32,
4407 ) -> Result<u32>;
4408
4409 fn stream_read(
4411 &mut self,
4412 instance: Instance,
4413 caller: RuntimeComponentInstanceIndex,
4414 ty: TypeStreamTableIndex,
4415 options: OptionsIndex,
4416 stream: u32,
4417 address: u32,
4418 count: u32,
4419 ) -> Result<u32>;
4420
4421 fn flat_stream_write(
4424 &mut self,
4425 instance: Instance,
4426 caller: RuntimeComponentInstanceIndex,
4427 ty: TypeStreamTableIndex,
4428 options: OptionsIndex,
4429 payload_size: u32,
4430 payload_align: u32,
4431 stream: u32,
4432 address: u32,
4433 count: u32,
4434 ) -> Result<u32>;
4435
4436 fn flat_stream_read(
4439 &mut self,
4440 instance: Instance,
4441 caller: RuntimeComponentInstanceIndex,
4442 ty: TypeStreamTableIndex,
4443 options: OptionsIndex,
4444 payload_size: u32,
4445 payload_align: u32,
4446 stream: u32,
4447 address: u32,
4448 count: u32,
4449 ) -> Result<u32>;
4450
4451 fn stream_drop_writable(
4453 &mut self,
4454 instance: Instance,
4455 ty: TypeStreamTableIndex,
4456 writer: u32,
4457 ) -> Result<()>;
4458
4459 fn error_context_debug_message(
4461 &mut self,
4462 instance: Instance,
4463 ty: TypeComponentLocalErrorContextTableIndex,
4464 options: OptionsIndex,
4465 err_ctx_handle: u32,
4466 debug_msg_address: u32,
4467 ) -> Result<()>;
4468
4469 fn thread_new_indirect(
4471 &mut self,
4472 instance: Instance,
4473 caller: RuntimeComponentInstanceIndex,
4474 func_ty_idx: TypeFuncIndex,
4475 start_func_table_idx: RuntimeTableIndex,
4476 start_func_idx: u32,
4477 context: i32,
4478 ) -> Result<u32>;
4479}
4480
4481impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4483 unsafe fn prepare_call(
4484 &mut self,
4485 instance: Instance,
4486 memory: *mut VMMemoryDefinition,
4487 start: NonNull<VMFuncRef>,
4488 return_: NonNull<VMFuncRef>,
4489 caller_instance: RuntimeComponentInstanceIndex,
4490 callee_instance: RuntimeComponentInstanceIndex,
4491 task_return_type: TypeTupleIndex,
4492 callee_async: bool,
4493 string_encoding: StringEncoding,
4494 result_count_or_max_if_async: u32,
4495 storage: *mut ValRaw,
4496 storage_len: usize,
4497 ) -> Result<()> {
4498 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4502
4503 unsafe {
4504 instance.prepare_call(
4505 StoreContextMut(self),
4506 start,
4507 return_,
4508 caller_instance,
4509 callee_instance,
4510 task_return_type,
4511 callee_async,
4512 memory,
4513 string_encoding,
4514 match result_count_or_max_if_async {
4515 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4516 params,
4517 has_result: false,
4518 },
4519 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4520 params,
4521 has_result: true,
4522 },
4523 result_count => CallerInfo::Sync {
4524 params,
4525 result_count,
4526 },
4527 },
4528 )
4529 }
4530 }
4531
4532 unsafe fn sync_start(
4533 &mut self,
4534 instance: Instance,
4535 callback: *mut VMFuncRef,
4536 callee: NonNull<VMFuncRef>,
4537 param_count: u32,
4538 storage: *mut MaybeUninit<ValRaw>,
4539 storage_len: usize,
4540 ) -> Result<()> {
4541 unsafe {
4542 instance
4543 .start_call(
4544 StoreContextMut(self),
4545 callback,
4546 ptr::null_mut(),
4547 callee,
4548 param_count,
4549 1,
4550 START_FLAG_ASYNC_CALLEE,
4551 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4555 )
4556 .map(drop)
4557 }
4558 }
4559
4560 unsafe fn async_start(
4561 &mut self,
4562 instance: Instance,
4563 callback: *mut VMFuncRef,
4564 post_return: *mut VMFuncRef,
4565 callee: NonNull<VMFuncRef>,
4566 param_count: u32,
4567 result_count: u32,
4568 flags: u32,
4569 ) -> Result<u32> {
4570 unsafe {
4571 instance.start_call(
4572 StoreContextMut(self),
4573 callback,
4574 post_return,
4575 callee,
4576 param_count,
4577 result_count,
4578 flags,
4579 None,
4580 )
4581 }
4582 }
4583
4584 fn future_write(
4585 &mut self,
4586 instance: Instance,
4587 caller: RuntimeComponentInstanceIndex,
4588 ty: TypeFutureTableIndex,
4589 options: OptionsIndex,
4590 future: u32,
4591 address: u32,
4592 ) -> Result<u32> {
4593 instance
4594 .guest_write(
4595 StoreContextMut(self),
4596 caller,
4597 TransmitIndex::Future(ty),
4598 options,
4599 None,
4600 future,
4601 address,
4602 1,
4603 )
4604 .map(|result| result.encode())
4605 }
4606
4607 fn future_read(
4608 &mut self,
4609 instance: Instance,
4610 caller: RuntimeComponentInstanceIndex,
4611 ty: TypeFutureTableIndex,
4612 options: OptionsIndex,
4613 future: u32,
4614 address: u32,
4615 ) -> Result<u32> {
4616 instance
4617 .guest_read(
4618 StoreContextMut(self),
4619 caller,
4620 TransmitIndex::Future(ty),
4621 options,
4622 None,
4623 future,
4624 address,
4625 1,
4626 )
4627 .map(|result| result.encode())
4628 }
4629
4630 fn stream_write(
4631 &mut self,
4632 instance: Instance,
4633 caller: RuntimeComponentInstanceIndex,
4634 ty: TypeStreamTableIndex,
4635 options: OptionsIndex,
4636 stream: u32,
4637 address: u32,
4638 count: u32,
4639 ) -> Result<u32> {
4640 instance
4641 .guest_write(
4642 StoreContextMut(self),
4643 caller,
4644 TransmitIndex::Stream(ty),
4645 options,
4646 None,
4647 stream,
4648 address,
4649 count,
4650 )
4651 .map(|result| result.encode())
4652 }
4653
4654 fn stream_read(
4655 &mut self,
4656 instance: Instance,
4657 caller: RuntimeComponentInstanceIndex,
4658 ty: TypeStreamTableIndex,
4659 options: OptionsIndex,
4660 stream: u32,
4661 address: u32,
4662 count: u32,
4663 ) -> Result<u32> {
4664 instance
4665 .guest_read(
4666 StoreContextMut(self),
4667 caller,
4668 TransmitIndex::Stream(ty),
4669 options,
4670 None,
4671 stream,
4672 address,
4673 count,
4674 )
4675 .map(|result| result.encode())
4676 }
4677
4678 fn future_drop_writable(
4679 &mut self,
4680 instance: Instance,
4681 ty: TypeFutureTableIndex,
4682 writer: u32,
4683 ) -> Result<()> {
4684 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4685 }
4686
4687 fn flat_stream_write(
4688 &mut self,
4689 instance: Instance,
4690 caller: RuntimeComponentInstanceIndex,
4691 ty: TypeStreamTableIndex,
4692 options: OptionsIndex,
4693 payload_size: u32,
4694 payload_align: u32,
4695 stream: u32,
4696 address: u32,
4697 count: u32,
4698 ) -> Result<u32> {
4699 instance
4700 .guest_write(
4701 StoreContextMut(self),
4702 caller,
4703 TransmitIndex::Stream(ty),
4704 options,
4705 Some(FlatAbi {
4706 size: payload_size,
4707 align: payload_align,
4708 }),
4709 stream,
4710 address,
4711 count,
4712 )
4713 .map(|result| result.encode())
4714 }
4715
4716 fn flat_stream_read(
4717 &mut self,
4718 instance: Instance,
4719 caller: RuntimeComponentInstanceIndex,
4720 ty: TypeStreamTableIndex,
4721 options: OptionsIndex,
4722 payload_size: u32,
4723 payload_align: u32,
4724 stream: u32,
4725 address: u32,
4726 count: u32,
4727 ) -> Result<u32> {
4728 instance
4729 .guest_read(
4730 StoreContextMut(self),
4731 caller,
4732 TransmitIndex::Stream(ty),
4733 options,
4734 Some(FlatAbi {
4735 size: payload_size,
4736 align: payload_align,
4737 }),
4738 stream,
4739 address,
4740 count,
4741 )
4742 .map(|result| result.encode())
4743 }
4744
4745 fn stream_drop_writable(
4746 &mut self,
4747 instance: Instance,
4748 ty: TypeStreamTableIndex,
4749 writer: u32,
4750 ) -> Result<()> {
4751 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4752 }
4753
4754 fn error_context_debug_message(
4755 &mut self,
4756 instance: Instance,
4757 ty: TypeComponentLocalErrorContextTableIndex,
4758 options: OptionsIndex,
4759 err_ctx_handle: u32,
4760 debug_msg_address: u32,
4761 ) -> Result<()> {
4762 instance.error_context_debug_message(
4763 StoreContextMut(self),
4764 ty,
4765 options,
4766 err_ctx_handle,
4767 debug_msg_address,
4768 )
4769 }
4770
4771 fn thread_new_indirect(
4772 &mut self,
4773 instance: Instance,
4774 caller: RuntimeComponentInstanceIndex,
4775 func_ty_idx: TypeFuncIndex,
4776 start_func_table_idx: RuntimeTableIndex,
4777 start_func_idx: u32,
4778 context: i32,
4779 ) -> Result<u32> {
4780 instance.thread_new_indirect(
4781 StoreContextMut(self),
4782 caller,
4783 func_ty_idx,
4784 start_func_table_idx,
4785 start_func_idx,
4786 context,
4787 )
4788 }
4789}
4790
4791type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4792
4793pub(crate) struct HostTask {
4797 common: WaitableCommon,
4798
4799 caller: TableId<GuestTask>,
4806
4807 call_context: CallContext,
4810
4811 state: HostTaskState,
4812}
4813
4814enum HostTaskState {
4815 CalleeStarted,
4820
4821 CalleeRunning(JoinHandle),
4826
4827 CalleeFinished(LiftedResult),
4831
4832 CalleeDone { cancelled: bool },
4835}
4836
4837impl HostTask {
4838 fn new(caller: TableId<GuestTask>, state: HostTaskState) -> Self {
4839 Self {
4840 common: WaitableCommon::default(),
4841 call_context: CallContext::default(),
4842 caller,
4843 state,
4844 }
4845 }
4846}
4847
4848impl TableDebug for HostTask {
4849 fn type_name() -> &'static str {
4850 "HostTask"
4851 }
4852}
4853
4854type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4855
4856enum Caller {
4858 Host {
4860 tx: Option<oneshot::Sender<LiftedResult>>,
4862 host_future_present: bool,
4865 caller: CurrentThread,
4869 },
4870 Guest {
4872 thread: QualifiedThreadId,
4874 },
4875}
4876
4877struct LiftResult {
4880 lift: RawLift,
4881 ty: TypeTupleIndex,
4882 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4883 string_encoding: StringEncoding,
4884}
4885
4886#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4891pub(crate) struct QualifiedThreadId {
4892 task: TableId<GuestTask>,
4893 thread: TableId<GuestThread>,
4894}
4895
4896impl QualifiedThreadId {
4897 fn qualify(
4898 state: &mut ConcurrentState,
4899 thread: TableId<GuestThread>,
4900 ) -> Result<QualifiedThreadId> {
4901 Ok(QualifiedThreadId {
4902 task: state.get_mut(thread)?.parent_task,
4903 thread,
4904 })
4905 }
4906}
4907
4908impl fmt::Debug for QualifiedThreadId {
4909 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4910 f.debug_tuple("QualifiedThreadId")
4911 .field(&self.task.rep())
4912 .field(&self.thread.rep())
4913 .finish()
4914 }
4915}
4916
4917enum GuestThreadState {
4918 NotStartedImplicit,
4919 NotStartedExplicit(
4920 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4921 ),
4922 Running,
4923 Suspended(StoreFiber<'static>),
4924 Ready {
4925 fiber: StoreFiber<'static>,
4926 },
4927 Completed,
4928}
4929
4930impl fmt::Debug for GuestThreadState {
4931 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4932 match self {
4933 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
4934 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
4935 Self::Running => f.debug_tuple("Running").finish(),
4936 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
4937 Self::Ready { .. } => f.debug_struct("Ready").finish(),
4938 Self::Completed => f.debug_tuple("Completed").finish(),
4939 }
4940 }
4941}
4942
4943pub struct GuestThread {
4944 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
4947 parent_task: TableId<GuestTask>,
4949 wake_on_cancel: Option<TableId<WaitableSet>>,
4952 state: GuestThreadState,
4954 instance_rep: Option<u32>,
4957 sync_call_set: TableId<WaitableSet>,
4959 old_do_not_suspend: Option<bool>,
4962}
4963
4964impl GuestThread {
4965 fn from_instance(
4968 state: Pin<&mut ComponentInstance>,
4969 caller_instance: RuntimeComponentInstanceIndex,
4970 guest_thread: u32,
4971 ) -> Result<TableId<Self>> {
4972 let rep = state.instance_states().0[caller_instance]
4973 .thread_handle_table()
4974 .guest_thread_rep(guest_thread)?;
4975 Ok(TableId::new(rep))
4976 }
4977
4978 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
4979 let sync_call_set = state.push(WaitableSet {
4980 is_sync_call_set: true,
4981 ..WaitableSet::default()
4982 })?;
4983 Ok(Self {
4984 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
4985 parent_task,
4986 wake_on_cancel: None,
4987 state: GuestThreadState::NotStartedImplicit,
4988 instance_rep: None,
4989 sync_call_set,
4990 old_do_not_suspend: None,
4991 })
4992 }
4993
4994 fn new_explicit(
4995 state: &mut ConcurrentState,
4996 parent_task: TableId<GuestTask>,
4997 start_func: Box<
4998 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
4999 >,
5000 ) -> Result<Self> {
5001 let sync_call_set = state.push(WaitableSet {
5002 is_sync_call_set: true,
5003 ..WaitableSet::default()
5004 })?;
5005 Ok(Self {
5006 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5007 parent_task,
5008 wake_on_cancel: None,
5009 state: GuestThreadState::NotStartedExplicit(start_func),
5010 instance_rep: None,
5011 sync_call_set,
5012 old_do_not_suspend: None,
5013 })
5014 }
5015}
5016
5017impl TableDebug for GuestThread {
5018 fn type_name() -> &'static str {
5019 "GuestThread"
5020 }
5021}
5022
5023enum SyncResult {
5024 NotProduced,
5025 Produced(Option<ValRaw>),
5026 Taken,
5027}
5028
5029impl SyncResult {
5030 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5031 Ok(match mem::replace(self, SyncResult::Taken) {
5032 SyncResult::NotProduced => None,
5033 SyncResult::Produced(val) => Some(val),
5034 SyncResult::Taken => {
5035 bail_bug!("attempted to take a synchronous result that was already taken")
5036 }
5037 })
5038 }
5039}
5040
5041#[derive(Debug)]
5042enum HostFutureState {
5043 NotApplicable,
5044 Live,
5045 Dropped,
5046}
5047
5048pub(crate) struct GuestTask {
5050 common: WaitableCommon,
5052 lower_params: Option<RawLower>,
5054 lift_result: Option<LiftResult>,
5056 result: Option<LiftedResult>,
5059 callback: Option<CallbackFn>,
5062 caller: Caller,
5064 call_context: CallContext,
5069 sync_result: SyncResult,
5072 cancel_sent: bool,
5075 starting_sent: bool,
5078 instance: RuntimeInstance,
5085 event: Option<Event>,
5088 exited: bool,
5090 threads: HashSet<TableId<GuestThread>>,
5092 host_future_state: HostFutureState,
5095 async_typed: bool,
5098 async_lifted: bool,
5101
5102 decremented_interesting_task_count: bool,
5103 switch_item: Option<WorkItem>,
5104}
5105
5106impl GuestTask {
5107 fn already_lowered_parameters(&self) -> bool {
5108 self.lower_params.is_none()
5110 }
5111
5112 fn returned_or_cancelled(&self) -> bool {
5113 self.lift_result.is_none()
5115 }
5116
5117 fn ready_to_delete(&self) -> bool {
5118 let threads_completed = self.threads.is_empty();
5119 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5120 let pending_completion_event = matches!(
5121 self.common.event,
5122 Some(Event::Subtask {
5123 status: Status::Returned | Status::ReturnCancelled
5124 })
5125 );
5126 let ready = threads_completed
5127 && !has_sync_result
5128 && !pending_completion_event
5129 && !matches!(self.host_future_state, HostFutureState::Live);
5130 log::trace!(
5131 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5132 threads_completed,
5133 has_sync_result,
5134 pending_completion_event,
5135 self.host_future_state
5136 );
5137 ready
5138 }
5139
5140 fn new(
5141 state: &mut ConcurrentState,
5142 lower_params: RawLower,
5143 lift_result: LiftResult,
5144 caller: Caller,
5145 callback: Option<CallbackFn>,
5146 instance: RuntimeInstance,
5147 async_typed: bool,
5148 async_lifted: bool,
5149 ) -> Result<QualifiedThreadId> {
5150 let host_future_state = match &caller {
5151 Caller::Guest { .. } => HostFutureState::NotApplicable,
5152 Caller::Host {
5153 host_future_present,
5154 ..
5155 } => {
5156 if *host_future_present {
5157 HostFutureState::Live
5158 } else {
5159 HostFutureState::NotApplicable
5160 }
5161 }
5162 };
5163 let task = state.push(Self {
5164 common: WaitableCommon::default(),
5165 lower_params: Some(lower_params),
5166 lift_result: Some(lift_result),
5167 result: None,
5168 callback,
5169 caller,
5170 call_context: CallContext::default(),
5171 sync_result: SyncResult::NotProduced,
5172 cancel_sent: false,
5173 starting_sent: false,
5174 instance,
5175 event: None,
5176 exited: false,
5177 threads: HashSet::new(),
5178 host_future_state,
5179 async_typed,
5180 async_lifted,
5181 decremented_interesting_task_count: false,
5182 switch_item: None,
5183 })?;
5184 let new_thread = GuestThread::new_implicit(state, task)?;
5185 let thread = state.push(new_thread)?;
5186 state.get_mut(task)?.threads.insert(thread);
5187 state.interesting_tasks += 1;
5188 let thread = QualifiedThreadId { task, thread };
5189 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5190 Ok(thread)
5191 }
5192}
5193
5194impl TableDebug for GuestTask {
5195 fn type_name() -> &'static str {
5196 "GuestTask"
5197 }
5198}
5199
5200#[derive(Default)]
5202struct WaitableCommon {
5203 event: Option<Event>,
5205 set: Option<TableId<WaitableSet>>,
5207 handle: Option<u32>,
5209}
5210
5211#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5213enum Waitable {
5214 Host(TableId<HostTask>),
5216 Guest(TableId<GuestTask>),
5218 Transmit(TableId<TransmitHandle>),
5220}
5221
5222impl Waitable {
5223 fn from_instance(
5226 state: Pin<&mut ComponentInstance>,
5227 caller_instance: RuntimeComponentInstanceIndex,
5228 waitable: u32,
5229 ) -> Result<Self> {
5230 use crate::runtime::vm::component::Waitable;
5231
5232 let (waitable, kind) = state.instance_states().0[caller_instance]
5233 .handle_table()
5234 .waitable_rep(waitable)?;
5235
5236 Ok(match kind {
5237 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5238 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5239 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5240 })
5241 }
5242
5243 fn rep(&self) -> u32 {
5245 match self {
5246 Self::Host(id) => id.rep(),
5247 Self::Guest(id) => id.rep(),
5248 Self::Transmit(id) => id.rep(),
5249 }
5250 }
5251
5252 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5256 log::trace!("waitable {self:?} join set {set:?}");
5257
5258 let old = mem::replace(&mut self.common(state)?.set, set);
5259
5260 if let Some(old) = old {
5261 match *self {
5262 Waitable::Host(id) => state.remove_child(id, old),
5263 Waitable::Guest(id) => state.remove_child(id, old),
5264 Waitable::Transmit(id) => state.remove_child(id, old),
5265 }?;
5266
5267 state.get_mut(old)?.ready.remove(self);
5268 }
5269
5270 if let Some(set) = set {
5271 match *self {
5272 Waitable::Host(id) => state.add_child(id, set),
5273 Waitable::Guest(id) => state.add_child(id, set),
5274 Waitable::Transmit(id) => state.add_child(id, set),
5275 }?;
5276
5277 if self.common(state)?.event.is_some() {
5278 self.mark_ready(state)?;
5279 }
5280 }
5281
5282 Ok(())
5283 }
5284
5285 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5287 Ok(match self {
5288 Self::Host(id) => &mut state.get_mut(*id)?.common,
5289 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5290 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5291 })
5292 }
5293
5294 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5300 if self.common(state)?.set.is_some() {
5301 bail!(Trap::WaitableSyncAndAsync);
5302 }
5303 Ok(())
5304 }
5305
5306 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5310 log::trace!("set event for {self:?}: {event:?}");
5311 self.common(state)?.event = event;
5312 self.mark_ready(state)
5313 }
5314
5315 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5317 let common = self.common(state)?;
5318 let event = common.event.take();
5319 if let Some(set) = self.common(state)?.set {
5320 state.get_mut(set)?.ready.remove(self);
5321 }
5322
5323 Ok(event)
5324 }
5325
5326 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5330 if let Some(set) = self.common(state)?.set {
5331 let set_state = state.get_mut(set)?;
5332 set_state.ready.insert(*self);
5333
5334 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5335 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5336 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5337
5338 let item = match mode {
5339 WaitMode::Caller { fiber, callee } => {
5340 let item = WorkItem::ResumeFiber {
5352 instance: state.get_mut(thread.task)?.instance,
5353 thread,
5354 fiber,
5355 };
5356
5357 if let Some(Event::Subtask {
5358 status: Status::Starting,
5359 }) = &self.common(state)?.event
5360 {
5361 state.set_switch_item(item)?;
5365 } else {
5366 if state.get_mut(callee)?.switch_item.is_some() {
5367 bail_bug!(
5368 "`GuestTask::switch_item` is already `Some(_)` when we need \
5369 to deliver a subtask status update to the caller"
5370 );
5371 }
5372 state.get_mut(callee)?.switch_item = Some(item);
5373 }
5374 None
5375 }
5376 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5377 instance: state.get_mut(thread.task)?.instance,
5378 thread,
5379 fiber,
5380 }),
5381 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5382 instance: state.get_mut(thread.task)?.instance,
5383 call: GuestCall {
5384 thread,
5385 kind: GuestCallKind::DeliverEvent {
5386 instance,
5387 set: Some(set),
5388 },
5389 },
5390 }),
5391 };
5392
5393 if let Some(item) = item {
5394 state.push_high_priority(item);
5395 }
5396 }
5397 }
5398 Ok(())
5399 }
5400
5401 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5403 match self {
5404 Self::Host(task) => {
5405 log::trace!("delete host task {task:?}");
5406 state.delete(*task)?;
5407 }
5408 Self::Guest(task) => {
5409 log::trace!("delete guest task {task:?}");
5410 let task = state.delete(*task)?;
5411
5412 debug_assert!(task.decremented_interesting_task_count);
5419 }
5420 Self::Transmit(task) => {
5421 state.delete(*task)?;
5422 }
5423 }
5424
5425 Ok(())
5426 }
5427}
5428
5429impl fmt::Debug for Waitable {
5430 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5431 match self {
5432 Self::Host(id) => write!(f, "{id:?}"),
5433 Self::Guest(id) => write!(f, "{id:?}"),
5434 Self::Transmit(id) => write!(f, "{id:?}"),
5435 }
5436 }
5437}
5438
5439#[derive(Default)]
5441struct WaitableSet {
5442 ready: BTreeSet<Waitable>,
5444 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5446 is_sync_call_set: bool,
5449}
5450
5451impl TableDebug for WaitableSet {
5452 fn type_name() -> &'static str {
5453 "WaitableSet"
5454 }
5455}
5456
5457type RawLower =
5459 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5460
5461type RawLift = Box<
5463 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5464>;
5465
5466type LiftedResult = Box<dyn Any + Send + Sync>;
5470
5471struct DummyResult;
5474
5475#[derive(Default)]
5477pub struct ConcurrentInstanceState {
5478 backpressure: u16,
5480 do_not_enter: bool,
5482 do_not_suspend: bool,
5485 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5488}
5489
5490impl ConcurrentInstanceState {
5491 pub fn pending_is_empty(&self) -> bool {
5492 self.pending.is_empty()
5493 }
5494}
5495
5496#[derive(Debug, Copy, Clone)]
5497pub(crate) enum CurrentThread {
5498 Guest(QualifiedThreadId),
5501 Host(TableId<HostTask>),
5503 GuestTask(TableId<GuestTask>),
5507 None,
5509}
5510
5511impl CurrentThread {
5512 fn guest(&self) -> Option<&QualifiedThreadId> {
5513 match self {
5514 Self::Guest(id) => Some(id),
5515 _ => None,
5516 }
5517 }
5518
5519 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5520 match self {
5521 Self::Guest(id) => Some(id.task),
5522 Self::GuestTask(id) => Some(*id),
5523 _ => None,
5524 }
5525 }
5526
5527 fn host(&self) -> Option<TableId<HostTask>> {
5528 match self {
5529 Self::Host(id) => Some(*id),
5530 _ => None,
5531 }
5532 }
5533
5534 fn is_none(&self) -> bool {
5535 matches!(self, Self::None)
5536 }
5537}
5538
5539impl From<QualifiedThreadId> for CurrentThread {
5540 fn from(id: QualifiedThreadId) -> Self {
5541 Self::Guest(id)
5542 }
5543}
5544
5545impl From<TableId<HostTask>> for CurrentThread {
5546 fn from(id: TableId<HostTask>) -> Self {
5547 Self::Host(id)
5548 }
5549}
5550
5551enum Priority {
5552 Switch,
5553 High,
5554 Low,
5555}
5556
5557pub struct ConcurrentState {
5559 unforced_current_thread: CurrentThread,
5565
5566 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5571 table: AlwaysMut<ResourceTable>,
5573 switch_item: Option<WorkItem>,
5581 high_priority: VecDeque<WorkItem>,
5583 low_priority: VecDeque<WorkItem>,
5585 suspend_reason: Option<SuspendReason>,
5589 worker: Option<StoreFiber<'static>>,
5593 worker_item: Option<WorkerItem>,
5595
5596 global_error_context_ref_counts:
5609 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5610
5611 interesting_tasks: usize,
5624
5625 interesting_tasks_empty_waker: Option<Waker>,
5629
5630 ready_for_concurrent_call_waker: Option<Waker>,
5635
5636 event_loop_running: bool,
5638}
5639
5640impl Default for ConcurrentState {
5641 fn default() -> Self {
5642 Self {
5643 unforced_current_thread: CurrentThread::None,
5644 table: AlwaysMut::new(ResourceTable::new()),
5645 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5646 switch_item: None,
5647 high_priority: VecDeque::new(),
5648 low_priority: VecDeque::new(),
5649 suspend_reason: None,
5650 worker: None,
5651 worker_item: None,
5652 global_error_context_ref_counts: BTreeMap::new(),
5653 interesting_tasks: 0,
5654 interesting_tasks_empty_waker: None,
5655 ready_for_concurrent_call_waker: None,
5656 event_loop_running: false,
5657 }
5658 }
5659}
5660
5661impl ConcurrentState {
5662 pub(crate) fn take_fibers_and_futures(
5679 &mut self,
5680 fibers: &mut Vec<StoreFiber<'static>>,
5681 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5682 ) {
5683 let mut items = Vec::new();
5684 for entry in self.table.get_mut().iter_mut() {
5685 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5686 for mode in mem::take(&mut set.waiting).into_values() {
5687 match mode {
5688 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5689 fibers.push(fiber);
5690 }
5691 WaitMode::Callback(_) => {}
5692 }
5693 }
5694 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5695 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5696 mem::replace(&mut thread.state, GuestThreadState::Completed)
5697 {
5698 fibers.push(fiber);
5699 }
5700 } else if let Some(task) = entry.downcast_mut::<GuestTask>() {
5701 if let Some(item) = task.switch_item.take() {
5702 items.push(item);
5703 }
5704 }
5705 }
5706
5707 if let Some(fiber) = self.worker.take() {
5708 fibers.push(fiber);
5709 }
5710
5711 let mut handle_item = |item| match item {
5712 WorkItem::ResumeFiber { fiber, .. } => {
5713 fibers.push(fiber);
5714 }
5715 WorkItem::PushFuture(future) => {
5716 self.futures
5717 .get_mut()
5718 .as_mut()
5719 .unwrap()
5720 .push(future.into_inner());
5721 }
5722 WorkItem::ResumeThread { .. }
5723 | WorkItem::GuestCall { .. }
5724 | WorkItem::WorkerFunction(_) => {}
5725 };
5726
5727 for item in items {
5728 handle_item(item);
5729 }
5730 if let Some(item) = self.switch_item.take() {
5731 handle_item(item);
5732 }
5733 for item in mem::take(&mut self.high_priority) {
5734 handle_item(item);
5735 }
5736 for item in mem::take(&mut self.low_priority) {
5737 handle_item(item);
5738 }
5739
5740 if let Some(them) = self.futures.get_mut().take() {
5741 futures.push(them);
5742 }
5743 }
5744
5745 #[cfg(feature = "gc")]
5746 pub(crate) fn trace_fiber_roots(
5747 &mut self,
5748 modules: &ModuleRegistry,
5749 unwind: &dyn Unwind,
5750 gc_roots_list: &mut GcRootsList,
5751 ) {
5752 let ConcurrentState {
5753 table,
5754 worker,
5755 switch_item,
5756 high_priority,
5757 low_priority,
5758
5759 futures: _,
5763
5764 worker_item: _,
5766 unforced_current_thread: _,
5767 suspend_reason: _,
5768 global_error_context_ref_counts: _,
5769 interesting_tasks: _,
5770 interesting_tasks_empty_waker: _,
5771 ready_for_concurrent_call_waker: _,
5772 event_loop_running: _,
5773 } = self;
5774
5775 for entry in table.get_mut().iter_mut() {
5776 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5777 for mode in set.waiting.values_mut() {
5778 match mode {
5779 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5780 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5781 }
5782 WaitMode::Callback(_) => {}
5783 }
5784 }
5785 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5786 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5787 &mut thread.state
5788 {
5789 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5790 }
5791 }
5792 }
5793
5794 if let Some(fiber) = worker {
5795 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5796 }
5797
5798 let mut handle_item = |item: &mut WorkItem| match item {
5799 WorkItem::ResumeFiber { fiber, .. } => {
5800 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5801 }
5802 WorkItem::PushFuture(_future) => {
5803 }
5806 WorkItem::ResumeThread { .. }
5807 | WorkItem::GuestCall { .. }
5808 | WorkItem::WorkerFunction(_) => {}
5809 };
5810
5811 if let Some(item) = switch_item {
5812 handle_item(item);
5813 }
5814 for item in high_priority {
5815 handle_item(item);
5816 }
5817 for item in low_priority {
5818 handle_item(item);
5819 }
5820 }
5821
5822 fn push<V: Send + Sync + 'static>(
5823 &mut self,
5824 value: V,
5825 ) -> Result<TableId<V>, ResourceTableError> {
5826 self.table.get_mut().push(value).map(TableId::from)
5827 }
5828
5829 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5830 self.table.get_mut().get_mut(&Resource::from(id))
5831 }
5832
5833 pub fn add_child<T: 'static, U: 'static>(
5834 &mut self,
5835 child: TableId<T>,
5836 parent: TableId<U>,
5837 ) -> Result<(), ResourceTableError> {
5838 self.table
5839 .get_mut()
5840 .add_child(Resource::from(child), Resource::from(parent))
5841 }
5842
5843 pub fn remove_child<T: 'static, U: 'static>(
5844 &mut self,
5845 child: TableId<T>,
5846 parent: TableId<U>,
5847 ) -> Result<(), ResourceTableError> {
5848 self.table
5849 .get_mut()
5850 .remove_child(Resource::from(child), Resource::from(parent))
5851 }
5852
5853 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5854 self.table.get_mut().delete(Resource::from(id))
5855 }
5856
5857 fn push_future(&mut self, future: HostTaskFuture) {
5858 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5865 }
5866
5867 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
5868 log::trace!("set switch item: {item:?}");
5869
5870 if self.switch_item.is_some() {
5871 bail_bug!("switch item already set");
5872 }
5873
5874 self.switch_item = Some(item);
5875
5876 Ok(())
5877 }
5878
5879 fn push_high_priority(&mut self, item: WorkItem) {
5880 log::trace!("push high priority: {item:?}");
5881 self.high_priority.push_front(item);
5882 }
5883
5884 fn push_low_priority(&mut self, item: WorkItem) {
5885 log::trace!("push low priority: {item:?}");
5886 self.low_priority.push_front(item);
5887 }
5888
5889 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
5890 match priority {
5891 Priority::Switch => self.set_switch_item(item)?,
5892 Priority::High => self.push_high_priority(item),
5893 Priority::Low => self.push_low_priority(item),
5894 }
5895
5896 Ok(())
5897 }
5898
5899 fn promote_instance_local_thread_work_item(
5900 &mut self,
5901 current_instance: RuntimeInstance,
5902 ) -> Result<bool> {
5903 log::trace!("promote thread work items for {current_instance:?}");
5904
5905 self.promote_work_item_matching(|item: &WorkItem| {
5906 let result = match item {
5907 WorkItem::ResumeThread { instance, .. }
5908 | WorkItem::ResumeFiber { instance, .. }
5909 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
5910 _ => false,
5911 };
5912
5913 log::trace!("candidate {item:?}: {result}");
5914 result
5915 })
5916 }
5917
5918 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
5919 self.promote_work_item_matching(|item: &WorkItem| match item {
5920 WorkItem::ResumeThread {
5921 thread: item_thread,
5922 ..
5923 }
5924 | WorkItem::GuestCall {
5925 call:
5926 GuestCall {
5927 thread: item_thread,
5928 ..
5929 },
5930 ..
5931 } => *item_thread == thread,
5932 _ => false,
5933 })
5934 }
5935
5936 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
5937 where
5938 F: FnMut(&WorkItem) -> bool,
5939 {
5940 for item in mem::take(&mut self.high_priority).into_iter().rev() {
5945 if self.switch_item.is_none() && predicate(&item) {
5946 self.set_switch_item(item)?;
5947 } else {
5948 self.push_high_priority(item);
5949 }
5950 }
5951
5952 if self.switch_item.is_none() {
5953 for item in mem::take(&mut self.low_priority).into_iter().rev() {
5954 if self.switch_item.is_none() && predicate(&item) {
5955 self.set_switch_item(item)?;
5956 } else {
5957 self.push_low_priority(item);
5958 }
5959 }
5960 }
5961
5962 Ok(self.switch_item.is_some())
5963 }
5964
5965 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
5971 let (task, is_host) = (task >> 1, task & 1 == 1);
5972 if is_host {
5973 let task: TableId<HostTask> = TableId::new(task);
5974 Ok(&mut self.get_mut(task)?.call_context)
5975 } else {
5976 let task: TableId<GuestTask> = TableId::new(task);
5977 Ok(&mut self.get_mut(task)?.call_context)
5978 }
5979 }
5980
5981 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
5982 match self.futures.get_mut().as_mut() {
5983 Some(f) => Ok(f),
5984 None => bail_bug!("futures field of concurrent state is currently taken"),
5985 }
5986 }
5987
5988 pub(crate) fn table(&mut self) -> &mut ResourceTable {
5989 self.table.get_mut()
5990 }
5991
5992 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
5994 let task = match cur {
5995 CurrentThread::GuestTask(task) => task,
5996 CurrentThread::Guest(thread) => thread.task,
5997 CurrentThread::Host(id) => {
5998 return Some(CurrentThread::GuestTask(self.get_mut(id).ok()?.caller));
5999 }
6000 CurrentThread::None => return None,
6001 };
6002 let task = self.get_mut(task).ok()?;
6003 Some(match task.caller {
6004 Caller::Host { caller, .. } => caller,
6005 Caller::Guest { thread } => thread.into(),
6006 })
6007 }
6008}
6009
6010fn for_any_lower<
6013 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6014>(
6015 fun: F,
6016) -> F {
6017 fun
6018}
6019
6020fn for_any_lift<
6022 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6023>(
6024 fun: F,
6025) -> F {
6026 fun
6027}
6028
6029fn check_ambient_store(id: StoreId) {
6030 let message = "\
6031 `Future`s which depend on asynchronous component tasks, streams, or \
6032 futures to complete may only be polled from the event loop of the \
6033 store to which they belong. Please use \
6034 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6035 ";
6036 tls::try_get(|store| {
6037 let matched = match store {
6038 tls::TryGet::Some(store) => store.id() == id,
6039 tls::TryGet::Taken | tls::TryGet::None => false,
6040 };
6041
6042 if !matched {
6043 panic!("{message}")
6044 }
6045 });
6046}
6047
6048fn unpack_callback_code(code: u32) -> (u32, u32) {
6049 (code & 0xF, code >> 4)
6050}
6051
6052struct WaitableCheckParams {
6056 set: TableId<WaitableSet>,
6057 options: OptionsIndex,
6058 payload: u32,
6059}
6060
6061enum WaitableCheck {
6064 Wait,
6065 Poll,
6066}
6067
6068#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
6077pub struct GuestTaskId(TableId<GuestTask>);
6078
6079pub(crate) struct PreparedCall<R> {
6081 handle: Func,
6083 thread: QualifiedThreadId,
6085 param_count: usize,
6087 rx: oneshot::Receiver<LiftedResult>,
6090 runtime_instance: RuntimeInstance,
6092 _phantom: PhantomData<R>,
6093}
6094
6095impl<R> PreparedCall<R> {
6096 pub(crate) fn task_id(&self) -> TaskId {
6098 TaskId {
6099 task: self.thread.task,
6100 runtime_instance: self.runtime_instance,
6101 }
6102 }
6103}
6104
6105pub(crate) struct TaskId {
6107 task: TableId<GuestTask>,
6108 runtime_instance: RuntimeInstance,
6109}
6110
6111impl TaskId {
6112 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6118 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6119 let delete = if !task.already_lowered_parameters() {
6120 store.cancel_guest_subtask_without_lowered_parameters(
6121 self.runtime_instance,
6122 self.task,
6123 )?;
6124 true
6125 } else {
6126 task.host_future_state = HostFutureState::Dropped;
6127 task.ready_to_delete()
6128 };
6129 if delete {
6130 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
6131 }
6132 Ok(())
6133 }
6134}
6135
6136pub(crate) fn prepare_call<T, R>(
6142 mut store: StoreContextMut<T>,
6143 handle: Func,
6144 param_count: usize,
6145 host_future_present: bool,
6146 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6147 + Send
6148 + Sync
6149 + 'static,
6150 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6151 + Send
6152 + Sync
6153 + 'static,
6154) -> Result<PreparedCall<R>> {
6155 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6156
6157 let instance = handle.instance().id().get(store.0);
6158 let options = &instance.component().env_component().options[options];
6159 let ty = &instance.component().types()[ty];
6160 let async_typed = ty.async_;
6161 let async_lifted = raw_options.async_;
6162 let task_return_type = ty.results;
6163 let component_instance = raw_options.instance;
6164 let callback = options.callback.map(|i| instance.runtime_callback(i));
6165 let memory = options
6166 .memory()
6167 .map(|i| instance.runtime_memory(i))
6168 .map(SendSyncPtr::new);
6169 let string_encoding = options.string_encoding;
6170 let token = StoreToken::new(store.as_context_mut());
6171 let caller = store.0.current_thread()?;
6172 let state = store.0.concurrent_state_mut()?;
6173
6174 let (tx, rx) = oneshot::channel();
6175
6176 let instance = handle.instance().runtime_instance(component_instance);
6177 let thread = GuestTask::new(
6178 state,
6179 Box::new(for_any_lower(move |store, params| {
6180 lower_params(token.as_context_mut(store), params)
6181 })),
6182 LiftResult {
6183 lift: Box::new(for_any_lift(move |store, result| {
6184 lift_result(store, result)
6185 })),
6186 ty: task_return_type,
6187 memory,
6188 string_encoding,
6189 },
6190 Caller::Host {
6191 tx: Some(tx),
6192 host_future_present,
6193 caller,
6194 },
6195 callback.map(|callback| {
6196 let callback = SendSyncPtr::new(callback);
6197 let instance = handle.instance();
6198 Box::new(move |store: &mut dyn VMStore, event, handle| {
6199 let store = token.as_context_mut(store);
6200 unsafe { instance.call_callback(store, callback, event, handle) }
6203 }) as CallbackFn
6204 }),
6205 instance,
6206 async_typed,
6207 async_lifted,
6208 )?;
6209
6210 if !store.0.may_enter() {
6211 bail!(Trap::CannotEnterComponent);
6212 }
6213
6214 Ok(PreparedCall {
6215 handle,
6216 thread,
6217 param_count,
6218 runtime_instance: instance,
6219 rx,
6220 _phantom: PhantomData,
6221 })
6222}
6223
6224pub(crate) struct StagedCall<R> {
6225 store: StoreId,
6226 task: TableId<GuestTask>,
6227 rx: oneshot::Receiver<LiftedResult>,
6228 _marker: PhantomData<fn() -> R>,
6229}
6230
6231impl<R> StagedCall<R> {
6232 pub(crate) fn new<T: 'static>(
6239 mut store: StoreContextMut<T>,
6240 prepared: PreparedCall<R>,
6241 ) -> Result<StagedCall<R>> {
6242 let PreparedCall {
6243 handle,
6244 thread,
6245 param_count,
6246 rx,
6247 ..
6248 } = prepared;
6249
6250 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6251
6252 Ok(StagedCall {
6253 store: store.0.id(),
6254 task: thread.task,
6255 rx,
6256 _marker: PhantomData,
6257 })
6258 }
6259
6260 fn task(&self) -> GuestTaskId {
6261 GuestTaskId(self.task)
6262 }
6263}
6264
6265impl<R> Future for StagedCall<R>
6266where
6267 R: 'static,
6268{
6269 type Output = Result<R>;
6270
6271 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6272 check_ambient_store(self.store);
6273 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6274 Ok(r) => match r.downcast() {
6275 Ok(r) => Ok(*r),
6276 Err(_) => bail_bug!("wrong type of value produced"),
6277 },
6278 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6279 })
6280 }
6281}
6282
6283fn stage_call0<T: 'static>(
6286 store: StoreContextMut<T>,
6287 handle: Func,
6288 guest_thread: QualifiedThreadId,
6289 param_count: usize,
6290) -> Result<()> {
6291 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6292 let is_concurrent = raw_options.async_;
6293 let callback = raw_options.callback;
6294 let instance = handle.instance();
6295 let callee = handle.lifted_core_func(store.0);
6296 let post_return = raw_options
6297 .post_return
6298 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6299 let callback = callback.map(|i| {
6300 let instance = instance.id().get(store.0);
6301 SendSyncPtr::new(instance.runtime_callback(i))
6302 });
6303
6304 log::trace!("queueing call {guest_thread:?}");
6305
6306 unsafe {
6310 instance.stage_call(
6311 store,
6312 guest_thread,
6313 SendSyncPtr::new(callee),
6314 param_count,
6315 1,
6316 is_concurrent,
6317 callback,
6318 post_return.map(SendSyncPtr::new),
6319 true,
6320 )
6321 }
6322}