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 {
689 thread: QualifiedThreadId,
690 cancellable: bool,
691 },
692 ExplicitlySuspending { thread: QualifiedThreadId },
694}
695
696enum GuestCallKind {
698 DeliverEvent {
701 instance: Instance,
703 set: Option<TableId<WaitableSet>>,
708 },
709 StartImplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
715 StartExplicit(Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>),
716}
717
718impl fmt::Debug for GuestCallKind {
719 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
720 match self {
721 Self::DeliverEvent { instance, set } => f
722 .debug_struct("DeliverEvent")
723 .field("instance", instance)
724 .field("set", set)
725 .finish(),
726 Self::StartImplicit(_) => f.debug_tuple("StartImplicit").finish(),
727 Self::StartExplicit(_) => f.debug_tuple("StartExplicit").finish(),
728 }
729 }
730}
731
732#[derive(Copy, Clone, Debug)]
734pub enum SuspensionTarget {
735 Resume(u32),
736 Promote(u32),
737 None,
738}
739
740#[derive(Copy, Clone, Debug)]
742pub enum ResumeThread {
743 Promote,
744 Resume,
745 ResumeLater,
746}
747
748#[derive(Debug)]
750struct GuestCall {
751 thread: QualifiedThreadId,
752 kind: GuestCallKind,
753}
754
755impl GuestCall {
756 fn is_ready(&self, store: &mut StoreOpaque) -> Result<bool> {
766 let task = store.concurrent_state_mut()?.get_mut(self.thread.task)?;
767 let async_typed = task.async_typed;
768 let instance = task.instance;
769 let state = store.instance_state(instance).concurrent_state();
770
771 let ready = match &self.kind {
772 GuestCallKind::DeliverEvent { .. } => !state.do_not_enter,
773 GuestCallKind::StartImplicit(_) => {
774 !async_typed || !(state.do_not_enter || state.backpressure > 0)
775 }
776 GuestCallKind::StartExplicit(_) => true,
777 };
778 log::trace!(
779 "call {self:?} ready? {ready} (do_not_enter: {}; backpressure: {})",
780 state.do_not_enter,
781 state.backpressure
782 );
783 Ok(ready)
784 }
785}
786
787enum WorkerItem {
789 GuestCall(GuestCall),
790 Function(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
791}
792
793enum WorkItem {
796 PushFuture(AlwaysMut<HostTaskFuture>),
798 ResumeFiber {
800 instance: RuntimeInstance,
801 thread: QualifiedThreadId,
802 fiber: StoreFiber<'static>,
803 },
804 ResumeThread {
806 instance: RuntimeInstance,
807 thread: QualifiedThreadId,
808 },
809 GuestCall {
811 instance: RuntimeInstance,
812 call: GuestCall,
813 },
814 WorkerFunction(AlwaysMut<Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send>>),
816}
817
818impl fmt::Debug for WorkItem {
819 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
820 match self {
821 Self::PushFuture(_) => f.debug_tuple("PushFuture").finish(),
822 Self::ResumeFiber {
823 instance, thread, ..
824 } => f
825 .debug_struct("ResumeFiber")
826 .field("instance", instance)
827 .field("thread", thread)
828 .finish(),
829 Self::ResumeThread { instance, thread } => f
830 .debug_struct("ResumeThread")
831 .field("instance", instance)
832 .field("thread", thread)
833 .finish(),
834 Self::GuestCall { instance, call } => f
835 .debug_struct("GuestCall")
836 .field("instance", instance)
837 .field("call", call)
838 .finish(),
839 Self::WorkerFunction(_) => f.debug_tuple("WorkerFunction").finish(),
840 }
841 }
842}
843
844#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
846pub(crate) enum WaitResult {
847 Cancelled,
848 Completed,
849}
850
851pub(crate) fn poll_and_block<R: Send + Sync + 'static>(
859 store: &mut dyn VMStore,
860 host_task: EnteredHostTask,
861 future: impl Future<Output = Result<R>> + Send + 'static,
862) -> Result<R> {
863 let task = store.current_host_thread()?;
864
865 let mut future = Box::pin(async move {
869 let result = future.await?;
870 tls::get(move |store| {
871 let state = store.concurrent_state_mut()?;
872 let host_state = &mut state.get_mut(task)?.state;
873 assert!(matches!(host_state, HostTaskState::CalleeStarted));
874 *host_state = HostTaskState::CalleeFinished(Box::new(result));
875
876 Waitable::Host(task).set_event(
877 state,
878 Some(Event::Subtask {
879 status: Status::Returned,
880 }),
881 )?;
882
883 Ok(())
884 })
885 }) as HostTaskFuture;
886
887 let poll = tls::set(store, || {
891 future
892 .as_mut()
893 .poll(&mut Context::from_waker(&Waker::noop()))
894 });
895
896 let caller = match host_task {
897 Some(pair) => pair.1,
898 None => bail_bug!("host task wasn't created but should have been"),
899 };
900
901 match poll {
902 Poll::Ready(result) => result?,
904
905 Poll::Pending => {
910 let caller_instance = store.concurrent_state_mut()?.get_mut(caller.task)?.instance;
911 store.switch_or_trap_if_may_not_suspend(caller_instance)?;
912
913 let state = store.concurrent_state_mut()?;
914 state.push_future(future);
915
916 let set = state.get_mut(caller.thread)?.sync_call_set;
917 Waitable::Host(task).join(state, Some(set))?;
918
919 store.suspend(SuspendReason::Waiting {
920 set,
921 thread: caller,
922 })?;
923
924 Waitable::Host(task).join(store.concurrent_state_mut()?, None)?;
928 }
929 }
930
931 let host_state = &mut store.concurrent_state_mut()?.get_mut(task)?.state;
933 match mem::replace(host_state, HostTaskState::CalleeDone { cancelled: false }) {
934 HostTaskState::CalleeFinished(result) => Ok(match result.downcast() {
935 Ok(result) => *result,
936 Err(_) => bail_bug!("host task finished with wrong type of result"),
937 }),
938 _ => bail_bug!("unexpected host task state after completion"),
939 }
940}
941
942fn handle_guest_call(store: &mut dyn VMStore, call: GuestCall) -> Result<()> {
944 match call.kind {
945 GuestCallKind::DeliverEvent { instance, set } => {
946 let (event, waitable) = match instance.get_event(store, call.thread.task, set, true)? {
947 Some(pair) => pair,
948 None => bail_bug!("delivering non-present event"),
949 };
950 let state = store.concurrent_state_mut()?;
951 let task = state.get_mut(call.thread.task)?;
952 let runtime_instance = task.instance;
953 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
954
955 log::trace!(
956 "use callback to deliver event {event:?} to {:?} for {waitable:?}",
957 call.thread,
958 );
959
960 let old_thread = store.set_thread(call.thread)?;
961 log::trace!(
962 "GuestCallKind::DeliverEvent: replaced {old_thread:?} with {:?} as current thread",
963 call.thread
964 );
965
966 store.enter_instance(runtime_instance);
967
968 let Some(callback) = store
969 .concurrent_state_mut()?
970 .get_mut(call.thread.task)?
971 .callback
972 .take()
973 else {
974 bail_bug!("guest task callback field not present")
975 };
976
977 let code = callback(store, event, handle)?;
978
979 store
980 .concurrent_state_mut()?
981 .get_mut(call.thread.task)?
982 .callback = Some(callback);
983
984 store.exit_instance(runtime_instance)?;
985
986 store.set_thread(old_thread)?;
987
988 instance.handle_callback_code(store, call.thread, runtime_instance.index, code)?;
989
990 log::trace!("GuestCallKind::DeliverEvent: restored {old_thread:?} as current thread");
991 }
992 GuestCallKind::StartImplicit(fun) => {
993 fun(store)?;
994 }
995 GuestCallKind::StartExplicit(fun) => {
996 fun(store)?;
997 }
998 }
999
1000 Ok(())
1001}
1002
1003impl<T> Store<T> {
1004 pub async fn run_concurrent<R>(&mut self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1006 where
1007 T: Send + 'static,
1008 {
1009 ensure!(
1010 self.as_context().0.concurrency_support(),
1011 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1012 );
1013 self.as_context_mut().run_concurrent(fun).await
1014 }
1015
1016 #[doc(hidden)]
1017 pub fn assert_concurrent_state_empty(&mut self) {
1018 self.as_context_mut().assert_concurrent_state_empty();
1019 }
1020
1021 #[doc(hidden)]
1022 pub fn concurrent_state_table_size(&mut self) -> usize {
1023 self.as_context_mut().concurrent_state_table_size()
1024 }
1025
1026 pub fn spawn(&mut self, task: impl AccessorTask<T, HasSelf<T>>) -> Result<JoinHandle>
1028 where
1029 T: 'static,
1030 {
1031 self.as_context_mut().spawn(task)
1032 }
1033}
1034
1035impl<T> StoreContextMut<'_, T> {
1036 #[doc(hidden)]
1047 pub fn assert_concurrent_state_empty(self) {
1048 let store = self.0;
1049 store
1050 .store_data_mut()
1051 .components
1052 .assert_instance_states_empty();
1053 let state = store.concurrent_state_mut().unwrap();
1054 assert!(
1055 state.table.get_mut().is_empty(),
1056 "non-empty table: {:?}",
1057 state.table.get_mut()
1058 );
1059 assert!(state.switch_item.is_none());
1060 assert!(state.high_priority.is_empty());
1061 assert!(state.low_priority.is_empty());
1062 assert!(state.unforced_current_thread.is_none());
1063 assert!(state.futures_mut().unwrap().is_empty());
1064 assert!(state.global_error_context_ref_counts.is_empty());
1065 }
1066
1067 #[doc(hidden)]
1072 pub fn concurrent_state_table_size(&mut self) -> usize {
1073 self.0
1074 .concurrent_state_mut()
1075 .unwrap()
1076 .table
1077 .get_mut()
1078 .iter_mut()
1079 .count()
1080 }
1081
1082 pub fn spawn(mut self, task: impl AccessorTask<T>) -> Result<JoinHandle>
1092 where
1093 T: 'static,
1094 {
1095 let accessor = Accessor::new(StoreToken::new(self.as_context_mut()));
1096 self.spawn_with_accessor(accessor, task)
1097 }
1098
1099 fn spawn_with_accessor<D>(
1102 self,
1103 accessor: Accessor<T, D>,
1104 task: impl AccessorTask<T, D>,
1105 ) -> Result<JoinHandle>
1106 where
1107 T: 'static,
1108 D: HasData + ?Sized,
1109 {
1110 let (handle, future) = JoinHandle::run(async move { task.run(&accessor).await });
1114 self.0
1115 .concurrent_state_mut()?
1116 .push_future(Box::pin(async move { future.await.unwrap_or(Ok(())) }));
1117 Ok(handle)
1118 }
1119
1120 pub async fn run_concurrent<R>(self, fun: impl AsyncFnOnce(&Accessor<T>) -> R) -> Result<R>
1204 where
1205 T: Send + 'static,
1206 {
1207 ensure!(
1208 self.0.concurrency_support(),
1209 "cannot use `run_concurrent` when Config::concurrency_support disabled",
1210 );
1211 self.do_run_concurrent(fun, false).await
1212 }
1213
1214 pub(super) async fn run_concurrent_trap_on_idle<R>(
1215 self,
1216 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1217 ) -> Result<R> {
1218 self.do_run_concurrent(fun, true).await
1219 }
1220
1221 async fn do_run_concurrent<R>(
1222 mut self,
1223 fun: impl AsyncFnOnce(&Accessor<T>) -> R,
1224 trap_on_idle: bool,
1225 ) -> Result<R> {
1226 debug_assert!(self.0.concurrency_support());
1227 check_recursive_run();
1228 let token = StoreToken::new(self.as_context_mut());
1229
1230 struct Dropper<'a, T: 'static, V> {
1231 store: StoreContextMut<'a, T>,
1232 value: ManuallyDrop<V>,
1233 }
1234
1235 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1236 fn drop(&mut self) {
1237 self.store
1238 .0
1239 .concurrent_state_mut_already_forced_current_thread()
1240 .event_loop_running = false;
1241
1242 tls::set(self.store.0, || {
1243 unsafe { ManuallyDrop::drop(&mut self.value) }
1248 });
1249 }
1250 }
1251
1252 let accessor = &Accessor::new(token);
1253 self.0
1254 .concurrent_state_mut_already_forced_current_thread()
1255 .event_loop_running = true;
1256 let dropper = &mut Dropper {
1257 store: self,
1258 value: ManuallyDrop::new(fun(accessor)),
1259 };
1260 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1262
1263 dropper
1264 .store
1265 .as_context_mut()
1266 .poll_until(future, trap_on_idle)
1267 .await
1268 }
1269
1270 async fn poll_until<R>(
1276 mut self,
1277 mut future: Pin<&mut impl Future<Output = R>>,
1278 trap_on_idle: bool,
1279 ) -> Result<R> {
1280 struct Reset<'a, T: 'static> {
1281 store: StoreContextMut<'a, T>,
1282 futures: Option<FuturesUnordered<HostTaskFuture>>,
1283 }
1284
1285 impl<'a, T> Drop for Reset<'a, T> {
1286 fn drop(&mut self) {
1287 if let Some(futures) = self.futures.take() {
1288 *self
1289 .store
1290 .0
1291 .concurrent_state_mut_already_forced_current_thread()
1292 .futures
1293 .get_mut() = Some(futures);
1294 }
1295 }
1296 }
1297
1298 loop {
1299 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1303 let mut reset = Reset {
1304 store: self.as_context_mut(),
1305 futures,
1306 };
1307 let mut next = match reset.futures.as_mut() {
1308 Some(f) => pin!(f.next()),
1309 None => bail_bug!("concurrent state missing futures field"),
1310 };
1311
1312 enum PollResult<R> {
1313 Complete(R),
1314 ProcessWork {
1315 ready: Option<WorkItem>,
1316 low_priority: bool,
1317 },
1318 }
1319
1320 let result = future::poll_fn(|cx| {
1321 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1324 return Poll::Ready(Ok(PollResult::Complete(value)));
1325 }
1326
1327 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1331 Poll::Ready(Some(output)) => {
1332 match output {
1333 Err(e) => return Poll::Ready(Err(e)),
1334 Ok(()) => {}
1335 }
1336 Poll::Ready(true)
1337 }
1338 Poll::Ready(None) => Poll::Ready(false),
1339 Poll::Pending => Poll::Pending,
1340 };
1341
1342 let state = reset.store.0.concurrent_state_mut()?;
1357 let mut ready = state.switch_item.take();
1358 let mut low_priority = false;
1359 if ready.is_none() {
1360 ready = state.high_priority.pop_back();
1361 if ready.is_none() {
1362 ready = state.low_priority.pop_back();
1363 low_priority = true;
1364 }
1365 }
1366 if ready.is_some() {
1367 return Poll::Ready(Ok(PollResult::ProcessWork {
1368 ready,
1369 low_priority,
1370 }));
1371 }
1372
1373 return match next {
1377 Poll::Ready(true) => {
1378 Poll::Ready(Ok(PollResult::ProcessWork {
1384 ready: None,
1385 low_priority: false,
1386 }))
1387 }
1388 Poll::Ready(false) => {
1389 if let Poll::Ready(value) =
1393 tls::set(reset.store.0, || future.as_mut().poll(cx))
1394 {
1395 Poll::Ready(Ok(PollResult::Complete(value)))
1396 } else {
1397 if trap_on_idle {
1403 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1410 Trap::CannotBlockSyncTask.into()
1411 } else {
1412 Trap::AsyncDeadlock.into()
1414 }))
1415 } else {
1416 Poll::Pending
1420 }
1421 }
1422 }
1423 Poll::Pending => Poll::Pending,
1428 };
1429 })
1430 .await;
1431
1432 drop(reset);
1436
1437 match result? {
1438 PollResult::Complete(value) => break Ok(value),
1441 PollResult::ProcessWork {
1444 ready,
1445 low_priority,
1446 } => {
1447 struct Dispose<'a, T: 'static> {
1448 store: StoreContextMut<'a, T>,
1449 ready: Option<WorkItem>,
1450 }
1451
1452 impl<'a, T> Drop for Dispose<'a, T> {
1453 fn drop(&mut self) {
1454 if let Some(item) = self.ready.take() {
1455 match item {
1456 WorkItem::ResumeFiber { mut fiber, .. } => {
1457 fiber.dispose(self.store.0)
1458 }
1459 WorkItem::PushFuture(future) => {
1460 tls::set(self.store.0, move || drop(future))
1461 }
1462 _ => {}
1463 }
1464 }
1465 }
1466 }
1467
1468 let mut dispose = Dispose {
1469 store: self.as_context_mut(),
1470 ready,
1471 };
1472
1473 if low_priority {
1495 dispose.store.0.yield_now().await
1496 }
1497
1498 if let Some(item) = dispose.ready.take() {
1499 dispose
1500 .store
1501 .as_context_mut()
1502 .handle_work_item(item)
1503 .await?;
1504 }
1505 }
1506 }
1507 }
1508 }
1509
1510 async fn handle_work_item(self, item: WorkItem) -> Result<()> {
1512 log::trace!("handle work item {item:?}");
1513 match item {
1514 WorkItem::PushFuture(future) => {
1515 self.0
1516 .concurrent_state_mut()?
1517 .futures_mut()?
1518 .push(future.into_inner());
1519 }
1520 WorkItem::ResumeFiber { fiber, .. } => {
1521 self.0.resume_fiber(fiber).await?;
1522 }
1523 WorkItem::ResumeThread { thread, .. } => {
1524 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1525 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1526 GuestThreadState::Running,
1527 ) {
1528 self.0.resume_fiber(fiber).await?;
1529 } else {
1530 bail_bug!("cannot resume non-pending thread {thread:?}");
1531 }
1532 }
1533 WorkItem::GuestCall { call, .. } => {
1534 if call.is_ready(self.0)? {
1535 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1536 } else {
1537 let state = self.0.concurrent_state_mut()?;
1538 let task = state.get_mut(call.thread.task)?;
1539 if !task.starting_sent {
1540 task.starting_sent = true;
1541 if let GuestCallKind::StartImplicit(_) = &call.kind {
1542 Waitable::Guest(call.thread.task).set_event(
1543 state,
1544 Some(Event::Subtask {
1545 status: Status::Starting,
1546 }),
1547 )?;
1548 }
1549 }
1550
1551 let instance = state.get_mut(call.thread.task)?.instance;
1552 self.0
1553 .instance_state(instance)
1554 .concurrent_state()
1555 .pending
1556 .insert(call.thread, call.kind);
1557 }
1558 }
1559 WorkItem::WorkerFunction(fun) => {
1560 self.run_on_worker(WorkerItem::Function(fun)).await?;
1561 }
1562 }
1563
1564 Ok(())
1565 }
1566
1567 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1569 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1570 fiber
1571 } else {
1572 unsafe {
1591 fiber::make_fiber_unchecked(self.0, move |store| {
1592 loop {
1593 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1594 bail_bug!("worker_item not present when resuming fiber")
1595 };
1596 match item {
1597 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1598 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1599 }
1600
1601 store.suspend(SuspendReason::NeedWork)?;
1602 }
1603 })?
1604 }
1605 };
1606
1607 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1608 assert!(worker_item.is_none());
1609 *worker_item = Some(item);
1610
1611 self.0.resume_fiber(worker).await
1612 }
1613
1614 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1619 where
1620 T: 'static,
1621 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1622 + Send
1623 + Sync
1624 + 'static,
1625 R: Send + Sync + 'static,
1626 {
1627 let token = StoreToken::new(self);
1628 async move {
1629 let mut accessor = Accessor::new(token);
1630 closure(&mut accessor).await
1631 }
1632 }
1633
1634 pub fn async_call_stack(&mut self) -> Result<impl Iterator<Item = GuestTaskId>> {
1644 let mut cur = Some(self.0.current_thread()?);
1645 let state = self.0.concurrent_state_mut()?;
1646 Ok(core::iter::from_fn(move || {
1647 while let Some(t) = cur {
1648 cur = state.parent(t);
1649 if let Some(task) = t.guest_task() {
1650 return Some(GuestTaskId(task));
1651 }
1652 }
1653
1654 None
1655 }))
1656 }
1657
1658 pub(crate) async fn start_instance(
1659 &mut self,
1660 instance: ModuleInstance,
1661 ) -> Result<ModuleInstance> {
1662 let (tx, rx) = oneshot::channel();
1663 let token = StoreToken::new(self.as_context_mut());
1664 self.0.queue_task(move |store| {
1665 _ = tx.send(
1666 instance
1667 .start_raw(&mut token.as_context_mut(store))
1668 .map(|()| instance),
1669 );
1670 Ok(())
1671 })?;
1672 self.as_context_mut()
1673 .run_concurrent_trap_on_idle(async |_| {
1674 rx.await
1675 .map_err(|_| format_err!("oneshot channel canceled"))
1676 })
1677 .await??
1678 }
1679}
1680
1681pub type EnteredHostTask = Option<(TableId<HostTask>, QualifiedThreadId)>;
1687
1688impl StoreOpaque {
1689 #[inline]
1692 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1693 if !self.concurrency_support() {
1695 return Ok(CurrentThread::None);
1696 }
1697
1698 if !self
1701 .vm_store_context_mut()
1702 .current_thread_mut()
1703 .is_deferred()
1704 {
1705 return Ok(self
1706 .concurrent_state_mut_already_forced_current_thread()
1707 .unforced_current_thread);
1708 }
1709
1710 self.force_deferred_current_thread()
1711 }
1712
1713 #[cold]
1716 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1717 let state = self.concurrent_state_mut_without_forcing_current_thread();
1726 let id = match state.unforced_current_thread.guest_task() {
1727 Some(task) => state.get_mut(task)?.instance.instance,
1728 None => bail_bug!("deferred component-model thread with non-guest base"),
1729 };
1730
1731 let mut frames = Vec::new();
1734 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1735 while let Some(ptr) = cur.as_deferred() {
1736 let deferred = unsafe { ptr.as_non_null().as_ref() };
1741 frames.push((
1742 deferred.callee_async != 0,
1743 deferred.callee_instance,
1744 deferred.saved_context,
1745 ));
1746 cur = deferred.parent;
1747 }
1748
1749 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1753
1754 let current_context = *self.vm_store_context_mut().component_context_mut();
1757
1758 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1762 *self.vm_store_context_mut().component_context_mut() = saved_context;
1766 let callee = RuntimeInstance {
1767 instance: id,
1768 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1769 };
1770 self.enter_guest_sync_call(None, callee_async, callee)?;
1771 }
1772
1773 *self.vm_store_context_mut().component_context_mut() = current_context;
1775
1776 Ok(self
1777 .concurrent_state_mut_without_forcing_current_thread()
1778 .unforced_current_thread)
1779 }
1780
1781 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1782 match self.current_thread()?.guest() {
1783 Some(id) => Ok(*id),
1784 None => bail_bug!("current thread is not a guest thread"),
1785 }
1786 }
1787
1788 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1789 match self.current_thread()?.host() {
1790 Some(id) => Ok(id),
1791 None => bail_bug!("current thread is not a host thread"),
1792 }
1793 }
1794
1795 fn take_pending_cancellation(&mut self) -> Result<bool> {
1798 let thread = self.current_guest_thread()?;
1799 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1800 if let Some(Event::Cancelled) = task.event {
1801 task.event.take();
1802 return Ok(true);
1803 }
1804 Ok(false)
1805 }
1806
1807 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1808 log::trace!("enter sync-typed call {callee:?}");
1809 let state = self.instance_state(callee).concurrent_state();
1810 let old_do_not_suspend = state.do_not_suspend;
1811 state.do_not_suspend = true;
1812
1813 let thread = self.current_guest_thread()?;
1814 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1815 if thread.old_do_not_suspend.is_some() {
1816 bail_bug!("current thread already has `old_do_not_suspend` value");
1817 }
1818
1819 thread.old_do_not_suspend = Some(old_do_not_suspend);
1820
1821 Ok(())
1822 }
1823
1824 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1825 log::trace!("exit sync-typed call {callee:?}");
1826 let thread = self.current_guest_thread()?;
1827 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1828 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1829 bail_bug!("current thread missing `old_do_not_suspend` value");
1830 };
1831 let state = self.instance_state(callee).concurrent_state();
1832 state.do_not_suspend = old_do_not_suspend;
1833 Ok(())
1834 }
1835
1836 pub(crate) fn enter_guest_sync_call(
1848 &mut self,
1849 guest_caller: Option<RuntimeInstance>,
1850 callee_async_typed: bool,
1851 callee: RuntimeInstance,
1852 ) -> Result<()> {
1853 log::trace!("enter sync-lifted call {callee:?}");
1854 if !self.concurrency_support() {
1855 return self.enter_call_not_concurrent();
1856 }
1857
1858 let thread = self.current_thread()?;
1859 let state = self.concurrent_state_mut()?;
1860 let instance = if let Some(task) = thread.guest_task() {
1861 Some(state.get_mut(task)?.instance)
1862 } else {
1863 None
1864 };
1865 if guest_caller.is_some() {
1866 debug_assert_eq!(instance, guest_caller);
1867 }
1868 let guest_thread = GuestTask::new(
1869 state,
1870 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1871 LiftResult {
1872 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1873 ty: TypeTupleIndex::reserved_value(),
1874 memory: None,
1875 string_encoding: StringEncoding::Utf8,
1876 },
1877 if let Some(thread) = thread.guest() {
1878 Caller::Guest { thread: *thread }
1879 } else {
1880 Caller::Host {
1881 tx: None,
1882 host_future_present: false,
1883 caller: thread,
1884 }
1885 },
1886 None,
1887 callee,
1888 callee_async_typed,
1889 true,
1890 )?;
1891
1892 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1893 guest_thread.thread,
1894 self,
1895 callee.index,
1896 )?;
1897 self.set_thread(guest_thread)?;
1898
1899 if !callee_async_typed {
1900 self.enter_sync_call(callee)?;
1901 }
1902
1903 Ok(())
1904 }
1905
1906 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1914 if !self.concurrency_support() {
1915 return Ok(self.exit_call_not_concurrent());
1916 }
1917
1918 let thread = match self.current_thread()?.guest() {
1919 Some(t) => *t,
1920 None => bail_bug!("expected task when exiting"),
1921 };
1922 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1923 let instance = task.instance;
1924
1925 let caller = match &task.caller {
1926 &Caller::Guest { thread } => thread.into(),
1927 &Caller::Host { caller, .. } => caller,
1928 };
1929 task.lift_result = None;
1930 task.exited = true;
1931 let async_typed = task.async_typed;
1932
1933 if !async_typed {
1934 self.exit_sync_call(instance)?;
1935 }
1936
1937 self.set_thread(caller)?;
1938
1939 log::trace!("exit sync-lifted call {instance:?}");
1940
1941 if async_typed {
1942 self.switch_or_trap_if_may_not_suspend(instance)?;
1947 }
1948
1949 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1950
1951 Ok(())
1952 }
1953
1954 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
1962 if !self.concurrency_support() {
1963 self.enter_call_not_concurrent()?;
1964 return Ok(None);
1965 }
1966 let caller = self.current_guest_thread()?;
1967 let state = self.concurrent_state_mut()?;
1968 let task = state.push(HostTask::new(caller.task, HostTaskState::CalleeStarted))?;
1969 log::trace!("new host task {task:?}");
1970 self.set_thread(task)?;
1971 Ok(Some((task, caller)))
1972 }
1973
1974 pub(crate) fn host_task_delete(&mut self, task: EnteredHostTask) -> Result<()> {
1981 match task {
1982 Some((task, caller)) => {
1983 self.set_thread(caller)?;
1984 log::trace!("delete host task {task:?}");
1985 self.concurrent_state_mut()?.delete(task)?;
1986 }
1987 None => {
1988 self.exit_call_not_concurrent();
1989 }
1990 }
1991 Ok(())
1992 }
1993
1994 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
1997 self.component_instance_mut(instance.instance)
1998 .instance_state(instance.index)
1999 }
2000
2001 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2007 let thread = thread.into();
2008 let state = self.concurrent_state_mut()?;
2009 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2010
2011 if let Some(old_thread) = old_thread.guest() {
2019 let old_context = *self.vm_store_context_mut().component_context_mut();
2020 self.concurrent_state_mut()?
2021 .get_mut(old_thread.thread)?
2022 .context = old_context;
2023 }
2024 if cfg!(debug_assertions) {
2025 *self.vm_store_context_mut().component_context_mut() =
2026 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2027 }
2028 if let Some(thread) = thread.guest() {
2029 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2030 let context = thread.context;
2031 if cfg!(debug_assertions) {
2032 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2033 }
2034 *self.vm_store_context_mut().component_context_mut() = context;
2035 }
2036
2037 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2039 VMLazyThread::none()
2040 } else {
2041 VMLazyThread::forced()
2042 };
2043
2044 Ok(old_thread)
2045 }
2046
2047 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2049 if self.switch_if_may_not_suspend(instance)? {
2050 Ok(())
2051 } else {
2052 Err(Trap::CannotBlockSyncTask.into())
2053 }
2054 }
2055
2056 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2060 self.concurrent_state_mut()?;
2064
2065 Ok(!self.concurrency_support()
2066 || !self
2067 .instance_state(instance)
2068 .concurrent_state()
2069 .do_not_suspend
2070 || self
2071 .concurrent_state_mut()?
2072 .promote_instance_local_thread_work_item(instance)?)
2073 }
2074
2075 fn enter_instance(&mut self, instance: RuntimeInstance) {
2079 log::trace!("enter {instance:?}");
2080 self.instance_state(instance)
2081 .concurrent_state()
2082 .do_not_enter = true;
2083 }
2084
2085 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2089 log::trace!("exit {instance:?}");
2090 self.instance_state(instance)
2091 .concurrent_state()
2092 .do_not_enter = false;
2093 self.partition_pending(instance)
2094 }
2095
2096 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2104 for (thread, kind) in
2105 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2106 {
2107 let call = GuestCall { thread, kind };
2108 if call.is_ready(self)? {
2109 self.concurrent_state_mut()?
2110 .push_high_priority(WorkItem::GuestCall { instance, call });
2111 } else {
2112 self.instance_state(instance)
2113 .concurrent_state()
2114 .pending
2115 .insert(call.thread, call.kind);
2116 }
2117 }
2118
2119 if let Some(waker) = self
2120 .concurrent_state_mut()?
2121 .ready_for_concurrent_call_waker
2122 .take()
2123 {
2124 waker.wake();
2125 }
2126
2127 Ok(())
2128 }
2129
2130 pub(crate) fn backpressure_modify(
2132 &mut self,
2133 caller_instance: RuntimeInstance,
2134 modify: impl FnOnce(u16) -> Option<u16>,
2135 ) -> Result<()> {
2136 let state = self.instance_state(caller_instance).concurrent_state();
2137 let old = state.backpressure;
2138 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2139 state.backpressure = new;
2140
2141 if old > 0 && new == 0 {
2142 self.partition_pending(caller_instance)?;
2145 }
2146
2147 Ok(())
2148 }
2149
2150 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2153 let old_thread = self.current_thread()?;
2154 log::trace!("resume_fiber: save current thread {old_thread:?}");
2155
2156 let fiber = fiber::resolve_or_release(self, fiber).await?;
2157
2158 self.set_thread(old_thread)?;
2159
2160 let state = self.concurrent_state_mut()?;
2161
2162 if let Some(ot) = old_thread.guest() {
2163 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2164 }
2165 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2166
2167 if let Some(mut fiber) = fiber {
2168 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2169 let reason = match state.suspend_reason.take() {
2171 Some(r) => r,
2172 None => bail_bug!("suspend reason missing when resuming fiber"),
2173 };
2174 match reason {
2175 SuspendReason::NeedWork => {
2176 if state.worker.is_none() {
2177 state.worker = Some(fiber);
2178 } else {
2179 fiber.dispose(self);
2180 }
2181 }
2182 SuspendReason::Yielding {
2183 thread,
2184 cancellable,
2185 } => {
2186 state.get_mut(thread.thread)?.state =
2187 GuestThreadState::Ready { fiber, cancellable };
2188 let instance = state.get_mut(thread.task)?.instance;
2189 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2190 }
2191 SuspendReason::ExplicitlySuspending { thread } => {
2192 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2193 }
2194 SuspendReason::Waiting { set, thread } => {
2195 let old = state
2196 .get_mut(set)?
2197 .waiting
2198 .insert(thread, WaitMode::Fiber(fiber));
2199 assert!(old.is_none());
2200 }
2201 SuspendReason::WaitingForGuestSubtask { caller, callee } => {
2202 let set = state.get_mut(caller.thread)?.sync_call_set;
2203 let old = state
2204 .get_mut(set)?
2205 .waiting
2206 .insert(caller, WaitMode::Caller { fiber, callee });
2207 assert!(old.is_none());
2208 }
2209 };
2210 } else {
2211 log::trace!("resume_fiber: fiber has exited");
2212 }
2213
2214 Ok(())
2215 }
2216
2217 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2223 log::trace!("suspend fiber: {reason:?}");
2224
2225 let task = match &reason {
2229 SuspendReason::Yielding { thread, .. }
2230 | SuspendReason::Waiting { thread, .. }
2231 | SuspendReason::WaitingForGuestSubtask { caller: thread, .. }
2232 | SuspendReason::ExplicitlySuspending { thread } => Some(thread.task),
2233 SuspendReason::NeedWork => None,
2234 };
2235
2236 let old_guest_thread = if let Some(task) = task {
2237 let state = self.concurrent_state_mut()?;
2243 if state.switch_item.is_none() {
2244 if let Some(item) = state.get_mut(task)?.switch_item.take() {
2245 state.set_switch_item(item)?;
2246 }
2247 }
2248
2249 self.current_thread()?
2250 } else {
2251 CurrentThread::None
2252 };
2253
2254 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2255 assert!(suspend_reason.is_none());
2256 *suspend_reason = Some(reason);
2257
2258 if !self.fiber_async_state_mut().can_block() {
2261 return Err(format_err!("future dropped"));
2262 }
2263
2264 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2265
2266 if task.is_some() {
2267 self.set_thread(old_guest_thread)?;
2268 }
2269
2270 Ok(())
2271 }
2272
2273 fn wait_for_event(
2274 &mut self,
2275 caller_instance: RuntimeInstance,
2276 waitable: Waitable,
2277 reason: WaitReason,
2278 ) -> Result<()> {
2279 let caller = self.current_guest_thread()?;
2280 let state = self.concurrent_state_mut()?;
2281
2282 waitable.trap_if_in_waitable_set(state)?;
2283
2284 let set = state.get_mut(caller.thread)?.sync_call_set;
2285 waitable.join(state, Some(set))?;
2286
2287 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2288
2289 self.suspend(match reason {
2290 WaitReason::GuestSubtask(callee) => {
2291 SuspendReason::WaitingForGuestSubtask { caller, callee }
2292 }
2293 WaitReason::Other => SuspendReason::Waiting {
2294 set,
2295 thread: caller,
2296 },
2297 })?;
2298 let state = self.concurrent_state_mut()?;
2299 waitable.join(state, None)
2300 }
2301
2302 fn cleanup_thread(
2324 &mut self,
2325 guest_thread: QualifiedThreadId,
2326 runtime_instance: RuntimeInstance,
2327 cleanup_task: CleanupTask,
2328 ) -> Result<()> {
2329 let state = self.concurrent_state_mut()?;
2330 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2333 state.set_switch_item(item)?;
2334 }
2335 let thread_data = state.get_mut(guest_thread.thread)?;
2336 let sync_call_set = thread_data.sync_call_set;
2337 if let Some(guest_id) = thread_data.instance_rep {
2338 self.instance_state(runtime_instance)
2339 .thread_handle_table()
2340 .guest_thread_remove(guest_id)?;
2341 }
2342 let state = self.concurrent_state_mut()?;
2343
2344 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2346 if let Some(Event::Subtask {
2347 status: Status::Returned | Status::ReturnCancelled,
2348 }) = waitable.common(state)?.event
2349 {
2350 waitable.delete_from(state)?;
2351 }
2352 }
2353
2354 state.delete(guest_thread.thread)?;
2355 state.delete(sync_call_set)?;
2356 let task = state.get_mut(guest_thread.task)?;
2357 task.threads.remove(&guest_thread.thread);
2358
2359 if task.threads.is_empty() && !task.returned_or_cancelled() {
2360 bail!(Trap::NoAsyncResult);
2361 }
2362 let ready_to_delete = task.ready_to_delete();
2363
2364 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2365 task.decremented_interesting_task_count = true;
2366
2367 debug_assert!(state.interesting_tasks > 0);
2368 state.interesting_tasks -= 1;
2369 if state.interesting_tasks == 0
2370 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2371 {
2372 waker.wake();
2373 }
2374 }
2375
2376 match cleanup_task {
2377 CleanupTask::Yes => {
2378 if ready_to_delete {
2379 Waitable::Guest(guest_thread.task).delete_from(state)?;
2380 }
2381 }
2382 CleanupTask::No => {}
2383 }
2384
2385 Ok(())
2386 }
2387
2388 fn cancel_guest_subtask_without_lowered_parameters(
2401 &mut self,
2402 caller_instance: RuntimeInstance,
2403 guest_task: TableId<GuestTask>,
2404 ) -> Result<()> {
2405 let concurrent_state = self.concurrent_state_mut()?;
2406 let task = concurrent_state.get_mut(guest_task)?;
2407 assert!(!task.already_lowered_parameters());
2408 task.lower_params = None;
2412 task.lift_result = None;
2413 task.exited = true;
2414 let instance = task.instance;
2415
2416 assert_eq!(1, task.threads.len());
2419 let thread = *task.threads.iter().next().unwrap();
2420 self.cleanup_thread(
2421 QualifiedThreadId {
2422 task: guest_task,
2423 thread,
2424 },
2425 caller_instance,
2426 CleanupTask::No,
2427 )?;
2428
2429 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2431 let pending_count = pending.len();
2432 pending.retain(|thread, _| thread.task != guest_task);
2433 if pending.len() == pending_count {
2435 bail!(Trap::SubtaskCancelAfterTerminal);
2436 }
2437 Ok(())
2438 }
2439
2440 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2443 if !self.concurrency_support() {
2444 return self.current_scope_id_not_concurrent();
2445 }
2446 let (bits, is_host) = match self.current_thread()? {
2447 CurrentThread::Guest(id) => (id.task.rep(), false),
2448 CurrentThread::GuestTask(id) => (id.rep(), false),
2449 CurrentThread::Host(id) => (id.rep(), true),
2450 CurrentThread::None => return Ok(None),
2451 };
2452 assert_eq!((bits << 1) >> 1, bits);
2453 Ok(Some((bits << 1) | u32::from(is_host)))
2454 }
2455
2456 fn queue_task(
2457 &mut self,
2458 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2459 ) -> Result<()> {
2460 self.concurrent_state_mut()?
2461 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2462 Ok(())
2463 }
2464
2465 fn any_may_not_suspend(&mut self) -> Result<bool> {
2474 Ok(self
2482 .concurrent_state_mut()?
2483 .table
2484 .get_mut()
2485 .iter_mut()
2486 .filter_map(|entry| {
2487 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2488 Some(task.instance)
2489 } else {
2490 None
2491 }
2492 })
2493 .collect::<Vec<_>>()
2494 .into_iter()
2495 .any(|instance| {
2496 self.instance_state(instance)
2497 .concurrent_state()
2498 .do_not_suspend
2499 }))
2500 }
2501}
2502
2503enum CleanupTask {
2504 Yes,
2505 No,
2506}
2507
2508impl Instance {
2509 fn get_event(
2512 self,
2513 store: &mut StoreOpaque,
2514 guest_task: TableId<GuestTask>,
2515 set: Option<TableId<WaitableSet>>,
2516 cancellable: bool,
2517 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2518 let state = store.concurrent_state_mut()?;
2519
2520 let event = &mut state.get_mut(guest_task)?.event;
2521 if let Some(ev) = event
2522 && (cancellable || !matches!(ev, Event::Cancelled))
2523 {
2524 log::trace!("deliver event {ev:?} to {guest_task:?}");
2525 let ev = *ev;
2526 *event = None;
2527 return Ok(Some((ev, None)));
2528 }
2529
2530 let set = match set {
2531 Some(set) => set,
2532 None => return Ok(None),
2533 };
2534 let waitable = match state.get_mut(set)?.ready.pop_first() {
2535 Some(v) => v,
2536 None => return Ok(None),
2537 };
2538
2539 let common = waitable.common(state)?;
2540 let handle = match common.handle {
2541 Some(h) => h,
2542 None => bail_bug!("handle not set when delivering event"),
2543 };
2544 let event = match common.event.take() {
2545 Some(e) => e,
2546 None => bail_bug!("event not set when delivering event"),
2547 };
2548
2549 log::trace!(
2550 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2551 );
2552
2553 waitable.on_delivery(store, self, event)?;
2554
2555 Ok(Some((event, Some((waitable, handle)))))
2556 }
2557
2558 fn handle_callback_code(
2564 self,
2565 store: &mut StoreOpaque,
2566 guest_thread: QualifiedThreadId,
2567 runtime_instance: RuntimeComponentInstanceIndex,
2568 code: u32,
2569 ) -> Result<()> {
2570 let (code, set) = unpack_callback_code(code);
2571
2572 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2573
2574 let state = store.concurrent_state_mut()?;
2575
2576 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2577 state.set_switch_item(item)?;
2578 }
2579
2580 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2581 let set = store
2582 .instance_state(self.runtime_instance(runtime_instance))
2583 .handle_table()
2584 .waitable_set_rep(handle)?;
2585
2586 Ok(TableId::<WaitableSet>::new(set))
2587 };
2588
2589 match code {
2590 callback_code::EXIT => {
2591 log::trace!("implicit thread {guest_thread:?} completed");
2592 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2593 task.exited = true;
2594 task.callback = None;
2595
2596 let runtime_instance = self.runtime_instance(runtime_instance);
2597
2598 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2603
2604 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2605 }
2606 callback_code::YIELD => {
2607 let task = state.get_mut(guest_thread.task)?;
2608 if let Some(event) = task.event {
2613 assert!(matches!(event, Event::None | Event::Cancelled));
2614 } else {
2615 task.event = Some(Event::None);
2616 }
2617 let call = GuestCall {
2618 thread: guest_thread,
2619 kind: GuestCallKind::DeliverEvent {
2620 instance: self,
2621 set: None,
2622 },
2623 };
2624 state.push_low_priority(WorkItem::GuestCall {
2627 instance: self.runtime_instance(runtime_instance),
2628 call,
2629 });
2630 }
2631 callback_code::WAIT => {
2632 let set = get_set(store, set)?;
2633 let state = store.concurrent_state_mut()?;
2634
2635 if state.get_mut(guest_thread.task)?.event.is_some()
2636 || !state.get_mut(set)?.ready.is_empty()
2637 {
2638 state.push_high_priority(WorkItem::GuestCall {
2640 instance: self.runtime_instance(runtime_instance),
2641 call: GuestCall {
2642 thread: guest_thread,
2643 kind: GuestCallKind::DeliverEvent {
2644 instance: self,
2645 set: Some(set),
2646 },
2647 },
2648 });
2649 } else {
2650 let old = state
2658 .get_mut(guest_thread.thread)?
2659 .wake_on_cancel
2660 .replace(set);
2661 if !old.is_none() {
2662 bail_bug!("thread unexpectedly had wake_on_cancel set");
2663 }
2664 let old = state
2665 .get_mut(set)?
2666 .waiting
2667 .insert(guest_thread, WaitMode::Callback(self));
2668 if !old.is_none() {
2669 bail_bug!("set's waiting set already had this thread registered");
2670 }
2671 }
2672 }
2673 _ => bail!(Trap::UnsupportedCallbackCode),
2674 }
2675
2676 Ok(())
2677 }
2678
2679 unsafe fn stage_call<T: 'static>(
2686 self,
2687 mut store: StoreContextMut<T>,
2688 guest_thread: QualifiedThreadId,
2689 callee: SendSyncPtr<VMFuncRef>,
2690 param_count: usize,
2691 result_count: usize,
2692 async_: bool,
2693 callback: Option<SendSyncPtr<VMFuncRef>>,
2694 post_return: Option<SendSyncPtr<VMFuncRef>>,
2695 host_caller: bool,
2696 ) -> Result<()> {
2697 unsafe fn make_call<T: 'static>(
2712 store: StoreContextMut<T>,
2713 guest_thread: QualifiedThreadId,
2714 callee: SendSyncPtr<VMFuncRef>,
2715 param_count: usize,
2716 result_count: usize,
2717 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2718 + Send
2719 + Sync
2720 + 'static
2721 + use<T> {
2722 let token = StoreToken::new(store);
2723 move |store: &mut dyn VMStore| {
2724 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2725
2726 store
2727 .concurrent_state_mut()?
2728 .get_mut(guest_thread.thread)?
2729 .state = GuestThreadState::Running;
2730 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2731 let lower = match task.lower_params.take() {
2732 Some(l) => l,
2733 None => bail_bug!("lower_params missing"),
2734 };
2735
2736 lower(store, &mut storage[..param_count])?;
2737
2738 let mut store = token.as_context_mut(store);
2739
2740 unsafe {
2743 crate::Func::call_unchecked_raw(
2744 &mut store,
2745 callee.as_non_null(),
2746 NonNull::new(
2747 &mut storage[..param_count.max(result_count)]
2748 as *mut [MaybeUninit<ValRaw>] as _,
2749 )
2750 .unwrap(),
2751 )?;
2752 }
2753
2754 Ok(storage)
2755 }
2756 }
2757
2758 let call = unsafe {
2762 make_call(
2763 store.as_context_mut(),
2764 guest_thread,
2765 callee,
2766 param_count,
2767 result_count,
2768 )
2769 };
2770
2771 let callee_instance = store
2772 .0
2773 .concurrent_state_mut()?
2774 .get_mut(guest_thread.task)?
2775 .instance;
2776
2777 let fun = if callback.is_some() {
2778 assert!(async_);
2779
2780 Box::new(move |store: &mut dyn VMStore| {
2781 self.add_guest_thread_to_instance_table(
2782 guest_thread.thread,
2783 store,
2784 callee_instance.index,
2785 )?;
2786 let old_thread = store.set_thread(guest_thread)?;
2787 log::trace!(
2788 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2789 );
2790
2791 store.enter_instance(callee_instance);
2792
2793 let storage = call(store)?;
2800
2801 store.exit_instance(callee_instance)?;
2802
2803 store.set_thread(old_thread)?;
2804 let state = store.concurrent_state_mut()?;
2805 if let Some(t) = old_thread.guest() {
2806 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2807 }
2808 log::trace!("stackless call: restored {old_thread:?} as current thread");
2809
2810 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2813
2814 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2815 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2816 } else {
2817 let token = StoreToken::new(store.as_context_mut());
2818 Box::new(move |store: &mut dyn VMStore| {
2819 self.add_guest_thread_to_instance_table(
2820 guest_thread.thread,
2821 store,
2822 callee_instance.index,
2823 )?;
2824 let old_thread = store.set_thread(guest_thread)?;
2825 log::trace!(
2826 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2827 );
2828 let flags = self.id().get(store).instance_flags(callee_instance.index);
2829
2830 let callee_async_typed = store
2831 .concurrent_state_mut()?
2832 .get_mut(guest_thread.task)?
2833 .async_typed;
2834
2835 if !async_ && callee_async_typed {
2839 store.enter_instance(callee_instance);
2840 }
2841
2842 if !callee_async_typed {
2843 store.enter_sync_call(callee_instance)?;
2844 }
2845
2846 let storage = call(store)?;
2853
2854 if !callee_async_typed {
2855 store.exit_sync_call(callee_instance)?;
2856 }
2857
2858 if !async_ {
2859 if callee_async_typed {
2865 store.exit_instance(callee_instance)?;
2866 }
2867
2868 let lift = {
2869 let state = store.concurrent_state_mut()?;
2870 if !state.get_mut(guest_thread.task)?.result.is_none() {
2871 bail_bug!("task has already produced a result");
2872 }
2873
2874 match state.get_mut(guest_thread.task)?.lift_result.take() {
2875 Some(lift) => lift,
2876 None => bail_bug!("lift_result field is missing"),
2877 }
2878 };
2879
2880 let result = (lift.lift)(store, unsafe {
2883 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2884 &storage[..result_count],
2885 )
2886 })?;
2887
2888 let post_return_arg = match result_count {
2889 0 => ValRaw::i32(0),
2890 1 => unsafe { storage[0].assume_init() },
2893 _ => unreachable!(),
2894 };
2895
2896 unsafe {
2897 call_post_return(
2898 token.as_context_mut(store),
2899 post_return.map(|v| v.as_non_null()),
2900 post_return_arg,
2901 flags,
2902 )?;
2903 }
2904
2905 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2906 }
2907
2908 store.set_thread(old_thread)?;
2909
2910 store
2911 .concurrent_state_mut()?
2912 .get_mut(guest_thread.task)?
2913 .exited = true;
2914
2915 log::trace!(
2916 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
2917 );
2918
2919 if callee_async_typed {
2920 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
2925 }
2926
2927 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2929 Ok(())
2930 })
2931 };
2932
2933 store.0.concurrent_state_mut()?.push_work_item(
2934 WorkItem::GuestCall {
2935 instance: callee_instance,
2936 call: GuestCall {
2937 thread: guest_thread,
2938 kind: GuestCallKind::StartImplicit(fun),
2939 },
2940 },
2941 if host_caller {
2942 Priority::High
2943 } else {
2944 Priority::Switch
2945 },
2946 )?;
2947
2948 Ok(())
2949 }
2950
2951 unsafe fn prepare_call<T: 'static>(
2964 self,
2965 mut store: StoreContextMut<T>,
2966 start: NonNull<VMFuncRef>,
2967 return_: NonNull<VMFuncRef>,
2968 caller_instance: RuntimeComponentInstanceIndex,
2969 callee_instance: RuntimeComponentInstanceIndex,
2970 task_return_type: TypeTupleIndex,
2971 callee_async_typed: bool,
2972 memory: *mut VMMemoryDefinition,
2973 string_encoding: StringEncoding,
2974 caller_info: CallerInfo,
2975 ) -> Result<()> {
2976 enum ResultInfo {
2977 Heap { results: u32 },
2978 Stack { result_count: u32 },
2979 }
2980
2981 let result_info = match &caller_info {
2982 CallerInfo::Async {
2983 has_result: true,
2984 params,
2985 } => ResultInfo::Heap {
2986 results: match params.last() {
2987 Some(r) => r.get_u32(),
2988 None => bail_bug!("retptr missing"),
2989 },
2990 },
2991 CallerInfo::Async {
2992 has_result: false, ..
2993 } => ResultInfo::Stack { result_count: 0 },
2994 CallerInfo::Sync {
2995 result_count,
2996 params,
2997 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
2998 results: match params.last() {
2999 Some(r) => r.get_u32(),
3000 None => bail_bug!("arg ptr missing"),
3001 },
3002 },
3003 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3004 result_count: *result_count,
3005 },
3006 };
3007
3008 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3009
3010 let start = SendSyncPtr::new(start);
3014 let return_ = SendSyncPtr::new(return_);
3015 let token = StoreToken::new(store.as_context_mut());
3016 let old_thread = store.0.current_guest_thread()?;
3017 let state = store.0.concurrent_state_mut()?;
3018
3019 debug_assert_eq!(
3020 state.get_mut(old_thread.task)?.instance,
3021 self.runtime_instance(caller_instance)
3022 );
3023
3024 let guest_thread = GuestTask::new(
3025 state,
3026 Box::new(move |store, dst| {
3027 let mut store = token.as_context_mut(store);
3028 assert!(dst.len() <= MAX_FLAT_PARAMS);
3029 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3031 let count = match caller_info {
3032 CallerInfo::Async { params, has_result } => {
3036 let params = ¶ms[..params.len() - usize::from(has_result)];
3037 for (param, src) in params.iter().zip(&mut src) {
3038 src.write(*param);
3039 }
3040 params.len()
3041 }
3042
3043 CallerInfo::Sync { params, .. } => {
3045 for (param, src) in params.iter().zip(&mut src) {
3046 src.write(*param);
3047 }
3048 params.len()
3049 }
3050 };
3051 unsafe {
3058 crate::Func::call_unchecked_raw(
3059 &mut store,
3060 start.as_non_null(),
3061 NonNull::new(
3062 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3063 )
3064 .unwrap(),
3065 )?;
3066 }
3067 dst.copy_from_slice(&src[..dst.len()]);
3068 let task = store.0.current_guest_thread()?.task;
3069 let state = store.0.concurrent_state_mut()?;
3070 Waitable::Guest(task).set_event(
3071 state,
3072 Some(Event::Subtask {
3073 status: Status::Started,
3074 }),
3075 )?;
3076 Ok(())
3077 }),
3078 LiftResult {
3079 lift: Box::new(move |store, src| {
3080 let mut store = token.as_context_mut(store);
3083 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3085 my_src.push(ValRaw::u32(*results));
3086 }
3087
3088 unsafe {
3095 crate::Func::call_unchecked_raw(
3096 &mut store,
3097 return_.as_non_null(),
3098 my_src.as_mut_slice().into(),
3099 )?;
3100 }
3101
3102 let thread = store.0.current_guest_thread()?;
3103 let state = store.0.concurrent_state_mut()?;
3104 if sync_caller {
3105 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3106 if let ResultInfo::Stack { result_count } = &result_info {
3107 match result_count {
3108 0 => None,
3109 1 => Some(my_src[0]),
3110 _ => unreachable!(),
3111 }
3112 } else {
3113 None
3114 },
3115 );
3116 }
3117 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3118 }),
3119 ty: task_return_type,
3120 memory: NonNull::new(memory).map(SendSyncPtr::new),
3121 string_encoding,
3122 },
3123 Caller::Guest { thread: old_thread },
3124 None,
3125 self.runtime_instance(callee_instance),
3126 callee_async_typed,
3127 false,
3130 )?;
3131
3132 store.0.set_thread(guest_thread)?;
3135 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3136
3137 Ok(())
3138 }
3139
3140 unsafe fn call_callback<T>(
3145 self,
3146 mut store: StoreContextMut<T>,
3147 function: SendSyncPtr<VMFuncRef>,
3148 event: Event,
3149 handle: u32,
3150 ) -> Result<u32> {
3151 let (ordinal, result) = event.parts();
3152 let params = &mut [
3153 ValRaw::u32(ordinal),
3154 ValRaw::u32(handle),
3155 ValRaw::u32(result),
3156 ];
3157 unsafe {
3162 crate::Func::call_unchecked_raw(
3163 &mut store,
3164 function.as_non_null(),
3165 params.as_mut_slice().into(),
3166 )?;
3167 }
3168 Ok(params[0].get_u32())
3169 }
3170
3171 unsafe fn start_call<T: 'static>(
3184 self,
3185 mut store: StoreContextMut<T>,
3186 callback: *mut VMFuncRef,
3187 post_return: *mut VMFuncRef,
3188 callee: NonNull<VMFuncRef>,
3189 param_count: u32,
3190 result_count: u32,
3191 flags: u32,
3192 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3193 ) -> Result<u32> {
3194 let token = StoreToken::new(store.as_context_mut());
3195 let async_caller = storage.is_none();
3196 let guest_thread = store.0.current_guest_thread()?;
3197 let state = store.0.concurrent_state_mut()?;
3198
3199 if !state.event_loop_running {
3200 bail_bug!("Instance::start_call called without a running event loop");
3201 }
3202
3203 let callee = SendSyncPtr::new(callee);
3204 let param_count = usize::try_from(param_count)?;
3205 assert!(param_count <= MAX_FLAT_PARAMS);
3206 let result_count = usize::try_from(result_count)?;
3207 assert!(result_count <= MAX_FLAT_RESULTS);
3208
3209 let task = state.get_mut(guest_thread.task)?;
3210 let callee_async_typed = task.async_typed;
3211 let callee_instance = task.instance;
3212
3213 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3214
3215 if let Some(callback) = NonNull::new(callback) {
3216 let callback = SendSyncPtr::new(callback);
3220 task.callback = Some(Box::new(move |store, event, handle| {
3221 let store = token.as_context_mut(store);
3222 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3223 }));
3224 }
3225
3226 let Caller::Guest { thread: caller } = &task.caller else {
3227 bail_bug!("start_call unexpectedly invoked for host->guest call");
3230 };
3231 let caller = *caller;
3232 let caller_instance = state.get_mut(caller.task)?.instance;
3233
3234 unsafe {
3236 self.stage_call(
3237 store.as_context_mut(),
3238 guest_thread,
3239 callee,
3240 param_count,
3241 result_count,
3242 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3243 NonNull::new(callback).map(SendSyncPtr::new),
3244 NonNull::new(post_return).map(SendSyncPtr::new),
3245 false,
3246 )?;
3247 }
3248
3249 let old_do_not_suspend = if callee_async_typed {
3250 let state = store.0.instance_state(callee_instance).concurrent_state();
3257 let old_do_not_suspend = state.do_not_suspend;
3258 state.do_not_suspend = false;
3259 Some(old_do_not_suspend)
3260 } else {
3261 None
3262 };
3263
3264 let state = store.0.concurrent_state_mut()?;
3265
3266 let guest_waitable = Waitable::Guest(guest_thread.task);
3269 let old_set = guest_waitable.common(state)?.set;
3270 let set = state.get_mut(caller.thread)?.sync_call_set;
3271 guest_waitable.join(state, Some(set))?;
3272
3273 store.0.set_thread(CurrentThread::None)?;
3274
3275 let (status, waitable) = loop {
3291 store.0.suspend(SuspendReason::WaitingForGuestSubtask {
3292 caller,
3293 callee: guest_thread.task,
3294 })?;
3295
3296 if let Some(old_do_not_suspend) = old_do_not_suspend {
3297 store
3298 .0
3299 .instance_state(callee_instance)
3300 .concurrent_state()
3301 .do_not_suspend = old_do_not_suspend;
3302 }
3303
3304 let state = store.0.concurrent_state_mut()?;
3305
3306 log::trace!("taking event for {:?}", guest_thread.task);
3307 let event = guest_waitable.take_event(state)?;
3308 let Some(Event::Subtask { status }) = event else {
3309 bail_bug!("subtasks should only get subtask events, got {event:?}")
3310 };
3311
3312 log::trace!("status {status:?} for {:?}", guest_thread.task);
3313
3314 if status == Status::Returned {
3315 break (status, None);
3317 } else if async_caller {
3318 let handle = store
3322 .0
3323 .instance_state(caller_instance)
3324 .handle_table()
3325 .subtask_insert_guest(guest_thread.task.rep())?;
3326 store
3327 .0
3328 .concurrent_state_mut()?
3329 .get_mut(guest_thread.task)?
3330 .common
3331 .handle = Some(handle);
3332 break (status, Some(handle));
3333 } else {
3334 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3338 }
3339 };
3340
3341 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3342
3343 store.0.set_thread(caller)?;
3345 store
3346 .0
3347 .concurrent_state_mut()?
3348 .get_mut(caller.thread)?
3349 .state = GuestThreadState::Running;
3350 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3351
3352 if let Some(storage) = storage {
3353 let state = store.0.concurrent_state_mut()?;
3357 let task = state.get_mut(guest_thread.task)?;
3358 if let Some(result) = task.sync_result.take()? {
3359 if let Some(result) = result {
3360 storage[0] = MaybeUninit::new(result);
3361 }
3362
3363 if task.exited && task.ready_to_delete() {
3364 Waitable::Guest(guest_thread.task).delete_from(state)?;
3365 }
3366 }
3367 }
3368
3369 Ok(status.pack(waitable))
3370 }
3371
3372 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3385 self,
3386 mut store: StoreContextMut<'_, T>,
3387 host_task: EnteredHostTask,
3388 future: impl Future<Output = Result<R>> + Send + 'static,
3389 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool) -> Result<()> + Send + 'static,
3390 ) -> Result<u32> {
3391 let token = StoreToken::new(store.as_context_mut());
3392 let task = store.0.current_host_thread()?;
3393 let state = store.0.concurrent_state_mut()?;
3394
3395 let (join_handle, future) = JoinHandle::run(future);
3398 {
3399 let state = &mut state.get_mut(task)?.state;
3400 assert!(matches!(state, HostTaskState::CalleeStarted));
3401 *state = HostTaskState::CalleeRunning(join_handle);
3402 }
3403
3404 let mut future = Box::pin(future);
3405
3406 let poll = tls::set(store.0, || {
3411 future
3412 .as_mut()
3413 .poll(&mut Context::from_waker(&Waker::noop()))
3414 });
3415
3416 match poll {
3417 Poll::Ready(result) => {
3419 let result = result.transpose()?;
3420 lower(store.as_context_mut(), result, true)?;
3421 return Ok(Status::Returned.pack(None));
3422 }
3423
3424 Poll::Pending => {}
3426 }
3427
3428 let future = Box::pin(async move {
3436 let result = match future.await {
3437 Some(result) => Some(result?),
3438 None => None,
3439 };
3440 let on_complete = move |store: &mut dyn VMStore| {
3441 let mut store = token.as_context_mut(store);
3445 let old = store.0.set_thread(task)?;
3446
3447 let status = if result.is_some() {
3448 Status::Returned
3449 } else {
3450 Status::ReturnCancelled
3451 };
3452
3453 lower(store.as_context_mut(), result, false)?;
3454 let state = store.0.concurrent_state_mut()?;
3455 match &mut state.get_mut(task)?.state {
3456 HostTaskState::CalleeDone { .. } => {}
3459
3460 other => *other = HostTaskState::CalleeDone { cancelled: false },
3462 }
3463 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3464
3465 store.0.set_thread(old)?;
3466 Ok(())
3467 };
3468
3469 tls::get(move |store| {
3474 store
3475 .concurrent_state_mut()?
3476 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3477 on_complete,
3478 ))));
3479 Ok(())
3480 })
3481 });
3482
3483 let caller = match host_task {
3486 Some(pair) => pair.1,
3487 None => bail_bug!("host task wasn't created but should have been"),
3488 };
3489 let state = store.0.concurrent_state_mut()?;
3490 state.push_future(future);
3491 let instance = state.get_mut(caller.task)?.instance;
3492 let handle = store
3493 .0
3494 .instance_state(instance)
3495 .handle_table()
3496 .subtask_insert_host(task.rep())?;
3497 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3498 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3499
3500 store.0.set_thread(caller)?;
3504 Ok(Status::Started.pack(Some(handle)))
3505 }
3506
3507 pub(crate) fn task_return(
3510 self,
3511 store: &mut dyn VMStore,
3512 ty: TypeTupleIndex,
3513 options: OptionsIndex,
3514 storage: &[ValRaw],
3515 ) -> Result<()> {
3516 let guest_thread = store.current_guest_thread()?;
3517 let state = store.concurrent_state_mut()?;
3518 let lift = state
3519 .get_mut(guest_thread.task)?
3520 .lift_result
3521 .take()
3522 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3523 if !state.get_mut(guest_thread.task)?.result.is_none() {
3524 bail_bug!("task result unexpectedly already set");
3525 }
3526
3527 let CanonicalOptions {
3528 string_encoding,
3529 data_model,
3530 ..
3531 } = &self.id().get(store).component().env_component().options[options];
3532
3533 let invalid = ty != lift.ty
3534 || string_encoding != &lift.string_encoding
3535 || match data_model {
3536 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3537 Some(memory) => {
3538 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3539 let actual = self.id().get(store).runtime_memory(memory);
3540 expected != actual.as_ptr()
3541 }
3542 None => false,
3545 },
3546 CanonicalOptionsDataModel::Gc { .. } => true,
3548 };
3549
3550 if invalid {
3551 bail!(Trap::TaskReturnInvalid);
3552 }
3553
3554 log::trace!("task.return for {guest_thread:?}");
3555
3556 let result = (lift.lift)(store, storage)?;
3557 self.task_complete(store, guest_thread.task, result, Status::Returned)
3558 }
3559
3560 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3562 let guest_thread = store.current_guest_thread()?;
3563 let state = store.concurrent_state_mut()?;
3564 let task = state.get_mut(guest_thread.task)?;
3565 if !task.cancel_sent {
3566 bail!(Trap::TaskCancelNotCancelled);
3567 }
3568 _ = task
3569 .lift_result
3570 .take()
3571 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3572
3573 if !task.result.is_none() {
3574 bail_bug!("task result should not bet set yet");
3575 }
3576
3577 log::trace!("task.cancel for {guest_thread:?}");
3578
3579 self.task_complete(
3580 store,
3581 guest_thread.task,
3582 Box::new(DummyResult),
3583 Status::ReturnCancelled,
3584 )
3585 }
3586
3587 fn task_complete(
3593 self,
3594 store: &mut StoreOpaque,
3595 guest_task: TableId<GuestTask>,
3596 result: Box<dyn Any + Send + Sync>,
3597 status: Status,
3598 ) -> Result<()> {
3599 store
3600 .component_resource_tables(Some(self))?
3601 .validate_scope_exit()?;
3602
3603 let state = store.concurrent_state_mut()?;
3604 let task = state.get_mut(guest_task)?;
3605
3606 if let Caller::Host { tx, .. } = &mut task.caller {
3607 if let Some(tx) = tx.take() {
3608 _ = tx.send(result);
3609 }
3610 } else {
3611 task.result = Some(result);
3612 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3613 }
3614
3615 Ok(())
3616 }
3617
3618 pub(crate) fn waitable_set_new(
3620 self,
3621 store: &mut StoreOpaque,
3622 caller_instance: RuntimeComponentInstanceIndex,
3623 ) -> Result<u32> {
3624 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3625 let handle = store
3626 .instance_state(self.runtime_instance(caller_instance))
3627 .handle_table()
3628 .waitable_set_insert(set.rep())?;
3629 log::trace!("new waitable set {set:?} (handle {handle})");
3630 Ok(handle)
3631 }
3632
3633 pub(crate) fn waitable_set_drop(
3635 self,
3636 store: &mut StoreOpaque,
3637 caller_instance: RuntimeComponentInstanceIndex,
3638 set: u32,
3639 ) -> Result<()> {
3640 let rep = store
3641 .instance_state(self.runtime_instance(caller_instance))
3642 .handle_table()
3643 .waitable_set_remove(set)?;
3644
3645 log::trace!("drop waitable set {rep} (handle {set})");
3646
3647 if !store
3651 .concurrent_state_mut()?
3652 .get_mut(TableId::<WaitableSet>::new(rep))?
3653 .waiting
3654 .is_empty()
3655 {
3656 bail!(Trap::WaitableSetDropHasWaiters);
3657 }
3658
3659 store
3660 .concurrent_state_mut()?
3661 .delete(TableId::<WaitableSet>::new(rep))?;
3662
3663 Ok(())
3664 }
3665
3666 pub(crate) fn waitable_join(
3668 self,
3669 store: &mut StoreOpaque,
3670 caller_instance: RuntimeComponentInstanceIndex,
3671 waitable_handle: u32,
3672 set_handle: u32,
3673 ) -> Result<()> {
3674 let mut instance = self.id().get_mut(store);
3675 let waitable =
3676 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3677
3678 let set = if set_handle == 0 {
3679 None
3680 } else {
3681 let set = instance.instance_states().0[caller_instance]
3682 .handle_table()
3683 .waitable_set_rep(set_handle)?;
3684
3685 let state = store.concurrent_state_mut()?;
3686 if let Some(old) = waitable.common(state)?.set
3687 && state.get_mut(old)?.is_sync_call_set
3688 {
3689 bail!(Trap::WaitableSyncAndAsync);
3690 }
3691
3692 Some(TableId::<WaitableSet>::new(set))
3693 };
3694
3695 log::trace!(
3696 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3697 );
3698
3699 waitable.join(store.concurrent_state_mut()?, set)
3700 }
3701
3702 pub(crate) fn subtask_drop(
3704 self,
3705 store: &mut StoreOpaque,
3706 caller_instance: RuntimeComponentInstanceIndex,
3707 task_id: u32,
3708 ) -> Result<()> {
3709 self.waitable_join(store, caller_instance, task_id, 0)?;
3710
3711 let (rep, is_host) = store
3712 .instance_state(self.runtime_instance(caller_instance))
3713 .handle_table()
3714 .subtask_remove(task_id)?;
3715
3716 let concurrent_state = store.concurrent_state_mut()?;
3717 let (waitable, delete) = if is_host {
3718 let id = TableId::<HostTask>::new(rep);
3719 let task = concurrent_state.get_mut(id)?;
3720 match &task.state {
3721 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3722 HostTaskState::CalleeDone { .. } => {}
3723 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3724 bail_bug!("invalid state for callee in `subtask.drop`")
3725 }
3726 }
3727 (Waitable::Host(id), true)
3728 } else {
3729 let id = TableId::<GuestTask>::new(rep);
3730 let task = concurrent_state.get_mut(id)?;
3731 if task.lift_result.is_some() {
3732 bail!(Trap::SubtaskDropNotResolved);
3733 }
3734 (
3735 Waitable::Guest(id),
3736 concurrent_state.get_mut(id)?.ready_to_delete(),
3737 )
3738 };
3739
3740 waitable.common(concurrent_state)?.handle = None;
3741
3742 if waitable.take_event(concurrent_state)?.is_some() {
3745 bail!(Trap::SubtaskDropNotResolved);
3746 }
3747
3748 if delete {
3749 waitable.delete_from(concurrent_state)?;
3750 }
3751
3752 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3753 Ok(())
3754 }
3755
3756 pub(crate) fn waitable_set_wait(
3758 self,
3759 store: &mut StoreOpaque,
3760 options: OptionsIndex,
3761 set: u32,
3762 payload: u32,
3763 ) -> Result<u32> {
3764 let &CanonicalOptions {
3765 cancellable,
3766 instance: caller_instance,
3767 ..
3768 } = &self.id().get(store).component().env_component().options[options];
3769 let caller = self.runtime_instance(caller_instance);
3770 let rep = store
3771 .instance_state(self.runtime_instance(caller_instance))
3772 .handle_table()
3773 .waitable_set_rep(set)?;
3774
3775 self.waitable_check(
3776 store,
3777 caller,
3778 cancellable,
3779 WaitableCheck::Wait,
3780 WaitableCheckParams {
3781 set: TableId::new(rep),
3782 options,
3783 payload,
3784 },
3785 )
3786 }
3787
3788 pub(crate) fn waitable_set_poll(
3790 self,
3791 store: &mut StoreOpaque,
3792 options: OptionsIndex,
3793 set: u32,
3794 payload: u32,
3795 ) -> Result<u32> {
3796 let &CanonicalOptions {
3797 cancellable,
3798 instance: caller_instance,
3799 ..
3800 } = &self.id().get(store).component().env_component().options[options];
3801 let caller = self.runtime_instance(caller_instance);
3802 let rep = store
3803 .instance_state(caller)
3804 .handle_table()
3805 .waitable_set_rep(set)?;
3806
3807 self.waitable_check(
3808 store,
3809 caller,
3810 cancellable,
3811 WaitableCheck::Poll,
3812 WaitableCheckParams {
3813 set: TableId::new(rep),
3814 options,
3815 payload,
3816 },
3817 )
3818 }
3819
3820 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3822 let thread_id = store.current_guest_thread()?.thread;
3823 match store
3824 .concurrent_state_mut()?
3825 .get_mut(thread_id)?
3826 .instance_rep
3827 {
3828 Some(r) => Ok(r),
3829 None => bail_bug!("thread should have instance_rep by now"),
3830 }
3831 }
3832
3833 pub(crate) fn thread_new_indirect<T: 'static>(
3835 self,
3836 mut store: StoreContextMut<T>,
3837 runtime_instance: RuntimeComponentInstanceIndex,
3838 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3840 start_func_idx: u32,
3841 context: i32,
3842 ) -> Result<u32> {
3843 log::trace!("creating new thread");
3844
3845 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3846 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3847 let callee = instance
3848 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3849 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3850 if callee.type_index(store.0) != start_func_ty.type_index() {
3851 bail!(Trap::ThreadNewIndirectInvalidType);
3852 }
3853
3854 let token = StoreToken::new(store.as_context_mut());
3855 let start_func = Box::new(
3856 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3857 let old_thread = store.set_thread(guest_thread)?;
3858 log::trace!(
3859 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3860 );
3861
3862 let mut store = token.as_context_mut(store);
3863 let mut params = [ValRaw::i32(context)];
3864 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3867
3868 store.0.set_thread(old_thread)?;
3869
3870 let runtime_instance = self.runtime_instance(runtime_instance);
3871
3872 store
3875 .0
3876 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
3877
3878 store
3879 .0
3880 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
3881
3882 log::trace!("explicit thread {guest_thread:?} completed");
3883 let state = store.0.concurrent_state_mut()?;
3884 if let Some(t) = old_thread.guest() {
3885 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3886 }
3887 log::trace!("thread start: restored {old_thread:?} as current thread");
3888
3889 Ok(())
3890 },
3891 );
3892
3893 let current_thread = store.0.current_guest_thread()?;
3894 let state = store.0.concurrent_state_mut()?;
3895 let parent_task = current_thread.task;
3896
3897 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3898 let thread_id = state.push(new_thread)?;
3899 state.get_mut(parent_task)?.threads.insert(thread_id);
3900
3901 log::trace!("new thread with id {thread_id:?} created");
3902
3903 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3904 }
3905
3906 pub(crate) fn resume_thread(
3907 self,
3908 store: &mut StoreOpaque,
3909 runtime_instance: RuntimeComponentInstanceIndex,
3910 thread_idx: u32,
3911 how: ResumeThread,
3912 ) -> Result<bool> {
3913 let thread_id =
3914 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3915 let state = store.concurrent_state_mut()?;
3916 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3917
3918 if store.current_guest_thread()? == guest_thread {
3919 bail!(Trap::CannotResumeThread);
3920 }
3921
3922 let state = store.concurrent_state_mut()?;
3923 let thread = state.get_mut(guest_thread.thread)?;
3924 let priority = match how {
3925 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
3926 ResumeThread::ResumeLater => Priority::Low,
3927 };
3928
3929 match (&how, &thread.state) {
3930 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
3932 (ResumeThread::Promote, _) => return Ok(false),
3933
3934 (
3937 ResumeThread::Resume | ResumeThread::ResumeLater,
3938 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
3939 ) => {}
3940 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
3941 bail!(Trap::CannotResumeThread)
3942 }
3943 }
3944
3945 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3946 GuestThreadState::NotStartedExplicit(start_func) => {
3947 log::trace!("starting thread {guest_thread:?}");
3948 let guest_call = WorkItem::GuestCall {
3949 instance: self.runtime_instance(runtime_instance),
3950 call: GuestCall {
3951 thread: guest_thread,
3952 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3953 start_func(store, guest_thread)
3954 })),
3955 },
3956 };
3957 store
3958 .concurrent_state_mut()?
3959 .push_work_item(guest_call, priority)?;
3960 }
3961 GuestThreadState::Suspended(fiber) => {
3962 log::trace!("resuming thread {thread_id:?} that was suspended");
3963 store.concurrent_state_mut()?.push_work_item(
3964 WorkItem::ResumeFiber {
3965 instance: self.runtime_instance(runtime_instance),
3966 thread: guest_thread,
3967 fiber,
3968 },
3969 priority,
3970 )?;
3971 }
3972 GuestThreadState::Ready { fiber, cancellable } => {
3973 log::trace!("resuming thread {thread_id:?} that was ready");
3974 thread.state = GuestThreadState::Ready { fiber, cancellable };
3975 store
3976 .concurrent_state_mut()?
3977 .promote_thread_work_item(guest_thread)?;
3978 }
3979 other @ (GuestThreadState::NotStartedImplicit
3980 | GuestThreadState::Running
3981 | GuestThreadState::Completed) => {
3982 thread.state = other;
3983 }
3984 }
3985 Ok(true)
3986 }
3987
3988 fn add_guest_thread_to_instance_table(
3989 self,
3990 thread_id: TableId<GuestThread>,
3991 store: &mut StoreOpaque,
3992 runtime_instance: RuntimeComponentInstanceIndex,
3993 ) -> Result<u32> {
3994 let guest_id = store
3995 .instance_state(self.runtime_instance(runtime_instance))
3996 .thread_handle_table()
3997 .guest_thread_insert(thread_id.rep())?;
3998 store
3999 .concurrent_state_mut()?
4000 .get_mut(thread_id)?
4001 .instance_rep = Some(guest_id);
4002 Ok(guest_id)
4003 }
4004
4005 pub(crate) fn suspension_intrinsic(
4009 self,
4010 store: &mut StoreOpaque,
4011 caller: RuntimeComponentInstanceIndex,
4012 cancellable: bool,
4013 yielding: bool,
4014 to_thread: SuspensionTarget,
4015 ) -> Result<WaitResult> {
4016 if cancellable && store.take_pending_cancellation()? {
4018 return Ok(WaitResult::Cancelled);
4019 }
4020
4021 let check_suspend = match to_thread {
4022 SuspensionTarget::Promote(thread) => {
4023 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4024 }
4025 SuspensionTarget::Resume(thread) => {
4026 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4027 bail_bug!(
4028 "`resume_thread` should only ever return false \
4029 when `ResumeThread::Promote` is passed to it"
4030 );
4031 }
4032 false
4033 }
4034 SuspensionTarget::None => true,
4035 };
4036
4037 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4038 return if yielding {
4039 Ok(WaitResult::Completed)
4040 } else {
4041 Err(Trap::CannotBlockSyncTask.into())
4042 };
4043 }
4044
4045 let guest_thread = store.current_guest_thread()?;
4046
4047 let reason = if yielding {
4048 SuspendReason::Yielding {
4049 thread: guest_thread,
4050 cancellable,
4051 }
4052 } else {
4053 SuspendReason::ExplicitlySuspending {
4054 thread: guest_thread,
4055 }
4056 };
4057
4058 store.suspend(reason)?;
4059
4060 if cancellable && store.take_pending_cancellation()? {
4061 Ok(WaitResult::Cancelled)
4062 } else {
4063 Ok(WaitResult::Completed)
4064 }
4065 }
4066
4067 fn waitable_check(
4069 self,
4070 store: &mut StoreOpaque,
4071 caller: RuntimeInstance,
4072 cancellable: bool,
4073 check: WaitableCheck,
4074 params: WaitableCheckParams,
4075 ) -> Result<u32> {
4076 let guest_thread = store.current_guest_thread()?;
4077
4078 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4079
4080 let state = store.concurrent_state_mut()?;
4081 let task = state.get_mut(guest_thread.task)?;
4082
4083 match &check {
4086 WaitableCheck::Wait => {
4087 let set = params.set;
4088
4089 if (task.event.is_none()
4090 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
4091 && state.get_mut(set)?.ready.is_empty()
4092 {
4093 store.switch_or_trap_if_may_not_suspend(caller)?;
4094
4095 if cancellable {
4096 let old = store
4097 .concurrent_state_mut()?
4098 .get_mut(guest_thread.thread)?
4099 .wake_on_cancel
4100 .replace(set);
4101 if !old.is_none() {
4102 bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
4103 }
4104 }
4105
4106 store.suspend(SuspendReason::Waiting {
4107 set,
4108 thread: guest_thread,
4109 })?;
4110 }
4111 }
4112 WaitableCheck::Poll => {}
4113 }
4114
4115 log::trace!(
4116 "waitable check for {guest_thread:?}; set {:?}, part two",
4117 params.set
4118 );
4119
4120 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
4122
4123 let (ordinal, handle, result) = match &check {
4124 WaitableCheck::Wait => {
4125 let (event, waitable) = match event {
4126 Some(p) => p,
4127 None => bail_bug!("event expected to be present"),
4128 };
4129 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4130 let (ordinal, result) = event.parts();
4131 (ordinal, handle, result)
4132 }
4133 WaitableCheck::Poll => {
4134 if let Some((event, waitable)) = event {
4135 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4136 let (ordinal, result) = event.parts();
4137 (ordinal, handle, result)
4138 } else {
4139 log::trace!(
4140 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4141 guest_thread.task,
4142 params.set
4143 );
4144 let (ordinal, result) = Event::None.parts();
4145 (ordinal, 0, result)
4146 }
4147 }
4148 };
4149 let memory = self.options_memory_mut(store, params.options);
4150 let ptr = crate::component::func::validate_inbounds_dynamic(
4151 &CanonicalAbiInfo::POINTER_PAIR,
4152 memory,
4153 &ValRaw::u32(params.payload),
4154 )?;
4155 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4156 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4157 Ok(ordinal)
4158 }
4159
4160 pub(crate) fn subtask_cancel(
4162 self,
4163 store: &mut StoreOpaque,
4164 caller_instance: RuntimeComponentInstanceIndex,
4165 async_: bool,
4166 task_id: u32,
4167 ) -> Result<u32> {
4168 let (rep, is_host) = store
4169 .instance_state(self.runtime_instance(caller_instance))
4170 .handle_table()
4171 .subtask_rep(task_id)?;
4172 let waitable = if is_host {
4173 Waitable::Host(TableId::<HostTask>::new(rep))
4174 } else {
4175 Waitable::Guest(TableId::<GuestTask>::new(rep))
4176 };
4177 let concurrent_state = store.concurrent_state_mut()?;
4178
4179 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4180
4181 waitable.trap_if_in_waitable_set(concurrent_state)?;
4182
4183 let needs_block;
4184 if let Waitable::Host(host_task) = waitable {
4185 let state = &mut concurrent_state.get_mut(host_task)?.state;
4186 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
4187 HostTaskState::CalleeRunning(handle) => {
4194 handle.abort();
4195 needs_block = true;
4196 }
4197
4198 HostTaskState::CalleeDone { cancelled } => {
4201 if cancelled {
4202 bail!(Trap::SubtaskCancelAfterTerminal);
4203 } else {
4204 needs_block = false;
4207 }
4208 }
4209
4210 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4213 bail_bug!("invalid states for host callee")
4214 }
4215 }
4216 } else {
4217 let guest_task = TableId::<GuestTask>::new(rep);
4218 let task = concurrent_state.get_mut(guest_task)?;
4219 if !task.already_lowered_parameters() {
4220 store.cancel_guest_subtask_without_lowered_parameters(
4221 self.runtime_instance(caller_instance),
4222 guest_task,
4223 )?;
4224 return Ok(Status::StartCancelled as u32);
4225 } else if !task.returned_or_cancelled() {
4226 task.cancel_sent = true;
4229 task.event = Some(Event::Cancelled);
4234 let runtime_instance = task.instance;
4235 for thread in task.threads.clone() {
4236 let thread = QualifiedThreadId {
4237 task: guest_task,
4238 thread,
4239 };
4240 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4241
4242 let yield_ = |store: &mut StoreOpaque| {
4243 let state = store.instance_state(runtime_instance).concurrent_state();
4248 let old_do_not_suspend = state.do_not_suspend;
4249 state.do_not_suspend = false;
4250
4251 let caller = store.current_guest_thread()?;
4252
4253 let state = store.concurrent_state_mut()?;
4258 let set = state.get_mut(caller.thread)?.sync_call_set;
4259 waitable.join(state, Some(set))?;
4260
4261 store.suspend(SuspendReason::Yielding {
4262 thread: caller,
4263 cancellable: false,
4264 })?;
4265
4266 let state = store.concurrent_state_mut()?;
4267 waitable.join(state, None)?;
4268
4269 store
4270 .instance_state(runtime_instance)
4271 .concurrent_state()
4272 .do_not_suspend = old_do_not_suspend;
4273
4274 Ok::<(), crate::Error>(())
4275 };
4276
4277 if let Some(set) = thread_mut.wake_on_cancel.take() {
4278 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4280 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber {
4281 instance: runtime_instance,
4282 thread,
4283 fiber,
4284 },
4285 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4286 instance: runtime_instance,
4287 call: GuestCall {
4288 thread,
4289 kind: GuestCallKind::DeliverEvent {
4290 instance,
4291 set: None,
4292 },
4293 },
4294 },
4295 Some(WaitMode::Caller { .. }) => {
4296 bail_bug!("unexpected `WaitMode::Caller` in wake_on_cancel set")
4297 }
4298 None => bail_bug!("thread not present in wake_on_cancel set"),
4299 };
4300 concurrent_state.set_switch_item(item)?;
4301
4302 yield_(store)?;
4303
4304 break;
4305 } else if let GuestThreadState::Ready {
4306 cancellable: true, ..
4307 } = &thread_mut.state
4308 {
4309 if !concurrent_state.promote_thread_work_item(thread)? {
4312 bail_bug!("a ready thread should have been promotable");
4313 }
4314
4315 yield_(store)?;
4316
4317 break;
4318 }
4319 }
4320
4321 needs_block = !store
4324 .concurrent_state_mut()?
4325 .get_mut(guest_task)?
4326 .returned_or_cancelled()
4327 } else {
4328 needs_block = false;
4329 }
4330 };
4331
4332 if needs_block {
4336 if async_ {
4337 return Ok(BLOCKED);
4338 }
4339
4340 store.wait_for_event(
4343 self.runtime_instance(caller_instance),
4344 waitable,
4345 if is_host {
4346 WaitReason::Other
4347 } else {
4348 WaitReason::GuestSubtask(TableId::<GuestTask>::new(rep))
4349 },
4350 )?;
4351
4352 }
4354
4355 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4356 if let Some(Event::Subtask {
4357 status: status @ (Status::Returned | Status::ReturnCancelled),
4358 }) = event
4359 {
4360 Ok(status as u32)
4361 } else {
4362 bail!(Trap::SubtaskCancelAfterTerminal);
4363 }
4364 }
4365}
4366
4367pub trait VMComponentAsyncStore {
4375 unsafe fn prepare_call(
4381 &mut self,
4382 instance: Instance,
4383 memory: *mut VMMemoryDefinition,
4384 start: NonNull<VMFuncRef>,
4385 return_: NonNull<VMFuncRef>,
4386 caller_instance: RuntimeComponentInstanceIndex,
4387 callee_instance: RuntimeComponentInstanceIndex,
4388 task_return_type: TypeTupleIndex,
4389 callee_async: bool,
4390 string_encoding: StringEncoding,
4391 result_count: u32,
4392 storage: *mut ValRaw,
4393 storage_len: usize,
4394 ) -> Result<()>;
4395
4396 unsafe fn sync_start(
4399 &mut self,
4400 instance: Instance,
4401 callback: *mut VMFuncRef,
4402 callee: NonNull<VMFuncRef>,
4403 param_count: u32,
4404 storage: *mut MaybeUninit<ValRaw>,
4405 storage_len: usize,
4406 ) -> Result<()>;
4407
4408 unsafe fn async_start(
4411 &mut self,
4412 instance: Instance,
4413 callback: *mut VMFuncRef,
4414 post_return: *mut VMFuncRef,
4415 callee: NonNull<VMFuncRef>,
4416 param_count: u32,
4417 result_count: u32,
4418 flags: u32,
4419 ) -> Result<u32>;
4420
4421 fn future_write(
4423 &mut self,
4424 instance: Instance,
4425 caller: RuntimeComponentInstanceIndex,
4426 ty: TypeFutureTableIndex,
4427 options: OptionsIndex,
4428 future: u32,
4429 address: u32,
4430 ) -> Result<u32>;
4431
4432 fn future_read(
4434 &mut self,
4435 instance: Instance,
4436 caller: RuntimeComponentInstanceIndex,
4437 ty: TypeFutureTableIndex,
4438 options: OptionsIndex,
4439 future: u32,
4440 address: u32,
4441 ) -> Result<u32>;
4442
4443 fn future_drop_writable(
4445 &mut self,
4446 instance: Instance,
4447 ty: TypeFutureTableIndex,
4448 writer: u32,
4449 ) -> Result<()>;
4450
4451 fn stream_write(
4453 &mut self,
4454 instance: Instance,
4455 caller: RuntimeComponentInstanceIndex,
4456 ty: TypeStreamTableIndex,
4457 options: OptionsIndex,
4458 stream: u32,
4459 address: u32,
4460 count: u32,
4461 ) -> Result<u32>;
4462
4463 fn stream_read(
4465 &mut self,
4466 instance: Instance,
4467 caller: RuntimeComponentInstanceIndex,
4468 ty: TypeStreamTableIndex,
4469 options: OptionsIndex,
4470 stream: u32,
4471 address: u32,
4472 count: u32,
4473 ) -> Result<u32>;
4474
4475 fn flat_stream_write(
4478 &mut self,
4479 instance: Instance,
4480 caller: RuntimeComponentInstanceIndex,
4481 ty: TypeStreamTableIndex,
4482 options: OptionsIndex,
4483 payload_size: u32,
4484 payload_align: u32,
4485 stream: u32,
4486 address: u32,
4487 count: u32,
4488 ) -> Result<u32>;
4489
4490 fn flat_stream_read(
4493 &mut self,
4494 instance: Instance,
4495 caller: RuntimeComponentInstanceIndex,
4496 ty: TypeStreamTableIndex,
4497 options: OptionsIndex,
4498 payload_size: u32,
4499 payload_align: u32,
4500 stream: u32,
4501 address: u32,
4502 count: u32,
4503 ) -> Result<u32>;
4504
4505 fn stream_drop_writable(
4507 &mut self,
4508 instance: Instance,
4509 ty: TypeStreamTableIndex,
4510 writer: u32,
4511 ) -> Result<()>;
4512
4513 fn error_context_debug_message(
4515 &mut self,
4516 instance: Instance,
4517 ty: TypeComponentLocalErrorContextTableIndex,
4518 options: OptionsIndex,
4519 err_ctx_handle: u32,
4520 debug_msg_address: u32,
4521 ) -> Result<()>;
4522
4523 fn thread_new_indirect(
4525 &mut self,
4526 instance: Instance,
4527 caller: RuntimeComponentInstanceIndex,
4528 func_ty_idx: TypeFuncIndex,
4529 start_func_table_idx: RuntimeTableIndex,
4530 start_func_idx: u32,
4531 context: i32,
4532 ) -> Result<u32>;
4533}
4534
4535impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4537 unsafe fn prepare_call(
4538 &mut self,
4539 instance: Instance,
4540 memory: *mut VMMemoryDefinition,
4541 start: NonNull<VMFuncRef>,
4542 return_: NonNull<VMFuncRef>,
4543 caller_instance: RuntimeComponentInstanceIndex,
4544 callee_instance: RuntimeComponentInstanceIndex,
4545 task_return_type: TypeTupleIndex,
4546 callee_async: bool,
4547 string_encoding: StringEncoding,
4548 result_count_or_max_if_async: u32,
4549 storage: *mut ValRaw,
4550 storage_len: usize,
4551 ) -> Result<()> {
4552 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4556
4557 unsafe {
4558 instance.prepare_call(
4559 StoreContextMut(self),
4560 start,
4561 return_,
4562 caller_instance,
4563 callee_instance,
4564 task_return_type,
4565 callee_async,
4566 memory,
4567 string_encoding,
4568 match result_count_or_max_if_async {
4569 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4570 params,
4571 has_result: false,
4572 },
4573 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4574 params,
4575 has_result: true,
4576 },
4577 result_count => CallerInfo::Sync {
4578 params,
4579 result_count,
4580 },
4581 },
4582 )
4583 }
4584 }
4585
4586 unsafe fn sync_start(
4587 &mut self,
4588 instance: Instance,
4589 callback: *mut VMFuncRef,
4590 callee: NonNull<VMFuncRef>,
4591 param_count: u32,
4592 storage: *mut MaybeUninit<ValRaw>,
4593 storage_len: usize,
4594 ) -> Result<()> {
4595 unsafe {
4596 instance
4597 .start_call(
4598 StoreContextMut(self),
4599 callback,
4600 ptr::null_mut(),
4601 callee,
4602 param_count,
4603 1,
4604 START_FLAG_ASYNC_CALLEE,
4605 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4609 )
4610 .map(drop)
4611 }
4612 }
4613
4614 unsafe fn async_start(
4615 &mut self,
4616 instance: Instance,
4617 callback: *mut VMFuncRef,
4618 post_return: *mut VMFuncRef,
4619 callee: NonNull<VMFuncRef>,
4620 param_count: u32,
4621 result_count: u32,
4622 flags: u32,
4623 ) -> Result<u32> {
4624 unsafe {
4625 instance.start_call(
4626 StoreContextMut(self),
4627 callback,
4628 post_return,
4629 callee,
4630 param_count,
4631 result_count,
4632 flags,
4633 None,
4634 )
4635 }
4636 }
4637
4638 fn future_write(
4639 &mut self,
4640 instance: Instance,
4641 caller: RuntimeComponentInstanceIndex,
4642 ty: TypeFutureTableIndex,
4643 options: OptionsIndex,
4644 future: u32,
4645 address: u32,
4646 ) -> Result<u32> {
4647 instance
4648 .guest_write(
4649 StoreContextMut(self),
4650 caller,
4651 TransmitIndex::Future(ty),
4652 options,
4653 None,
4654 future,
4655 address,
4656 1,
4657 )
4658 .map(|result| result.encode())
4659 }
4660
4661 fn future_read(
4662 &mut self,
4663 instance: Instance,
4664 caller: RuntimeComponentInstanceIndex,
4665 ty: TypeFutureTableIndex,
4666 options: OptionsIndex,
4667 future: u32,
4668 address: u32,
4669 ) -> Result<u32> {
4670 instance
4671 .guest_read(
4672 StoreContextMut(self),
4673 caller,
4674 TransmitIndex::Future(ty),
4675 options,
4676 None,
4677 future,
4678 address,
4679 1,
4680 )
4681 .map(|result| result.encode())
4682 }
4683
4684 fn stream_write(
4685 &mut self,
4686 instance: Instance,
4687 caller: RuntimeComponentInstanceIndex,
4688 ty: TypeStreamTableIndex,
4689 options: OptionsIndex,
4690 stream: u32,
4691 address: u32,
4692 count: u32,
4693 ) -> Result<u32> {
4694 instance
4695 .guest_write(
4696 StoreContextMut(self),
4697 caller,
4698 TransmitIndex::Stream(ty),
4699 options,
4700 None,
4701 stream,
4702 address,
4703 count,
4704 )
4705 .map(|result| result.encode())
4706 }
4707
4708 fn stream_read(
4709 &mut self,
4710 instance: Instance,
4711 caller: RuntimeComponentInstanceIndex,
4712 ty: TypeStreamTableIndex,
4713 options: OptionsIndex,
4714 stream: u32,
4715 address: u32,
4716 count: u32,
4717 ) -> Result<u32> {
4718 instance
4719 .guest_read(
4720 StoreContextMut(self),
4721 caller,
4722 TransmitIndex::Stream(ty),
4723 options,
4724 None,
4725 stream,
4726 address,
4727 count,
4728 )
4729 .map(|result| result.encode())
4730 }
4731
4732 fn future_drop_writable(
4733 &mut self,
4734 instance: Instance,
4735 ty: TypeFutureTableIndex,
4736 writer: u32,
4737 ) -> Result<()> {
4738 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4739 }
4740
4741 fn flat_stream_write(
4742 &mut self,
4743 instance: Instance,
4744 caller: RuntimeComponentInstanceIndex,
4745 ty: TypeStreamTableIndex,
4746 options: OptionsIndex,
4747 payload_size: u32,
4748 payload_align: u32,
4749 stream: u32,
4750 address: u32,
4751 count: u32,
4752 ) -> Result<u32> {
4753 instance
4754 .guest_write(
4755 StoreContextMut(self),
4756 caller,
4757 TransmitIndex::Stream(ty),
4758 options,
4759 Some(FlatAbi {
4760 size: payload_size,
4761 align: payload_align,
4762 }),
4763 stream,
4764 address,
4765 count,
4766 )
4767 .map(|result| result.encode())
4768 }
4769
4770 fn flat_stream_read(
4771 &mut self,
4772 instance: Instance,
4773 caller: RuntimeComponentInstanceIndex,
4774 ty: TypeStreamTableIndex,
4775 options: OptionsIndex,
4776 payload_size: u32,
4777 payload_align: u32,
4778 stream: u32,
4779 address: u32,
4780 count: u32,
4781 ) -> Result<u32> {
4782 instance
4783 .guest_read(
4784 StoreContextMut(self),
4785 caller,
4786 TransmitIndex::Stream(ty),
4787 options,
4788 Some(FlatAbi {
4789 size: payload_size,
4790 align: payload_align,
4791 }),
4792 stream,
4793 address,
4794 count,
4795 )
4796 .map(|result| result.encode())
4797 }
4798
4799 fn stream_drop_writable(
4800 &mut self,
4801 instance: Instance,
4802 ty: TypeStreamTableIndex,
4803 writer: u32,
4804 ) -> Result<()> {
4805 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4806 }
4807
4808 fn error_context_debug_message(
4809 &mut self,
4810 instance: Instance,
4811 ty: TypeComponentLocalErrorContextTableIndex,
4812 options: OptionsIndex,
4813 err_ctx_handle: u32,
4814 debug_msg_address: u32,
4815 ) -> Result<()> {
4816 instance.error_context_debug_message(
4817 StoreContextMut(self),
4818 ty,
4819 options,
4820 err_ctx_handle,
4821 debug_msg_address,
4822 )
4823 }
4824
4825 fn thread_new_indirect(
4826 &mut self,
4827 instance: Instance,
4828 caller: RuntimeComponentInstanceIndex,
4829 func_ty_idx: TypeFuncIndex,
4830 start_func_table_idx: RuntimeTableIndex,
4831 start_func_idx: u32,
4832 context: i32,
4833 ) -> Result<u32> {
4834 instance.thread_new_indirect(
4835 StoreContextMut(self),
4836 caller,
4837 func_ty_idx,
4838 start_func_table_idx,
4839 start_func_idx,
4840 context,
4841 )
4842 }
4843}
4844
4845type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4846
4847pub(crate) struct HostTask {
4851 common: WaitableCommon,
4852
4853 caller: TableId<GuestTask>,
4860
4861 call_context: CallContext,
4864
4865 state: HostTaskState,
4866}
4867
4868enum HostTaskState {
4869 CalleeStarted,
4874
4875 CalleeRunning(JoinHandle),
4880
4881 CalleeFinished(LiftedResult),
4885
4886 CalleeDone { cancelled: bool },
4889}
4890
4891impl HostTask {
4892 fn new(caller: TableId<GuestTask>, state: HostTaskState) -> Self {
4893 Self {
4894 common: WaitableCommon::default(),
4895 call_context: CallContext::default(),
4896 caller,
4897 state,
4898 }
4899 }
4900}
4901
4902impl TableDebug for HostTask {
4903 fn type_name() -> &'static str {
4904 "HostTask"
4905 }
4906}
4907
4908type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4909
4910enum Caller {
4912 Host {
4914 tx: Option<oneshot::Sender<LiftedResult>>,
4916 host_future_present: bool,
4919 caller: CurrentThread,
4923 },
4924 Guest {
4926 thread: QualifiedThreadId,
4928 },
4929}
4930
4931struct LiftResult {
4934 lift: RawLift,
4935 ty: TypeTupleIndex,
4936 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4937 string_encoding: StringEncoding,
4938}
4939
4940#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4945pub(crate) struct QualifiedThreadId {
4946 task: TableId<GuestTask>,
4947 thread: TableId<GuestThread>,
4948}
4949
4950impl QualifiedThreadId {
4951 fn qualify(
4952 state: &mut ConcurrentState,
4953 thread: TableId<GuestThread>,
4954 ) -> Result<QualifiedThreadId> {
4955 Ok(QualifiedThreadId {
4956 task: state.get_mut(thread)?.parent_task,
4957 thread,
4958 })
4959 }
4960}
4961
4962impl fmt::Debug for QualifiedThreadId {
4963 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4964 f.debug_tuple("QualifiedThreadId")
4965 .field(&self.task.rep())
4966 .field(&self.thread.rep())
4967 .finish()
4968 }
4969}
4970
4971enum GuestThreadState {
4972 NotStartedImplicit,
4973 NotStartedExplicit(
4974 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4975 ),
4976 Running,
4977 Suspended(StoreFiber<'static>),
4978 Ready {
4979 fiber: StoreFiber<'static>,
4980 cancellable: bool,
4981 },
4982 Completed,
4983}
4984
4985impl fmt::Debug for GuestThreadState {
4986 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4987 match self {
4988 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
4989 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
4990 Self::Running => f.debug_tuple("Running").finish(),
4991 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
4992 Self::Ready { cancellable, .. } => f
4993 .debug_struct("Ready")
4994 .field("cancellable", cancellable)
4995 .finish(),
4996 Self::Completed => f.debug_tuple("Completed").finish(),
4997 }
4998 }
4999}
5000
5001pub struct GuestThread {
5002 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5005 parent_task: TableId<GuestTask>,
5007 wake_on_cancel: Option<TableId<WaitableSet>>,
5010 state: GuestThreadState,
5012 instance_rep: Option<u32>,
5015 sync_call_set: TableId<WaitableSet>,
5017 old_do_not_suspend: Option<bool>,
5020}
5021
5022impl GuestThread {
5023 fn from_instance(
5026 state: Pin<&mut ComponentInstance>,
5027 caller_instance: RuntimeComponentInstanceIndex,
5028 guest_thread: u32,
5029 ) -> Result<TableId<Self>> {
5030 let rep = state.instance_states().0[caller_instance]
5031 .thread_handle_table()
5032 .guest_thread_rep(guest_thread)?;
5033 Ok(TableId::new(rep))
5034 }
5035
5036 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5037 let sync_call_set = state.push(WaitableSet {
5038 is_sync_call_set: true,
5039 ..WaitableSet::default()
5040 })?;
5041 Ok(Self {
5042 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5043 parent_task,
5044 wake_on_cancel: None,
5045 state: GuestThreadState::NotStartedImplicit,
5046 instance_rep: None,
5047 sync_call_set,
5048 old_do_not_suspend: None,
5049 })
5050 }
5051
5052 fn new_explicit(
5053 state: &mut ConcurrentState,
5054 parent_task: TableId<GuestTask>,
5055 start_func: Box<
5056 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5057 >,
5058 ) -> Result<Self> {
5059 let sync_call_set = state.push(WaitableSet {
5060 is_sync_call_set: true,
5061 ..WaitableSet::default()
5062 })?;
5063 Ok(Self {
5064 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5065 parent_task,
5066 wake_on_cancel: None,
5067 state: GuestThreadState::NotStartedExplicit(start_func),
5068 instance_rep: None,
5069 sync_call_set,
5070 old_do_not_suspend: None,
5071 })
5072 }
5073}
5074
5075impl TableDebug for GuestThread {
5076 fn type_name() -> &'static str {
5077 "GuestThread"
5078 }
5079}
5080
5081enum SyncResult {
5082 NotProduced,
5083 Produced(Option<ValRaw>),
5084 Taken,
5085}
5086
5087impl SyncResult {
5088 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5089 Ok(match mem::replace(self, SyncResult::Taken) {
5090 SyncResult::NotProduced => None,
5091 SyncResult::Produced(val) => Some(val),
5092 SyncResult::Taken => {
5093 bail_bug!("attempted to take a synchronous result that was already taken")
5094 }
5095 })
5096 }
5097}
5098
5099#[derive(Debug)]
5100enum HostFutureState {
5101 NotApplicable,
5102 Live,
5103 Dropped,
5104}
5105
5106pub(crate) struct GuestTask {
5108 common: WaitableCommon,
5110 lower_params: Option<RawLower>,
5112 lift_result: Option<LiftResult>,
5114 result: Option<LiftedResult>,
5117 callback: Option<CallbackFn>,
5120 caller: Caller,
5122 call_context: CallContext,
5127 sync_result: SyncResult,
5130 cancel_sent: bool,
5133 starting_sent: bool,
5136 instance: RuntimeInstance,
5143 event: Option<Event>,
5146 exited: bool,
5148 threads: HashSet<TableId<GuestThread>>,
5150 host_future_state: HostFutureState,
5153 async_typed: bool,
5156 async_lifted: bool,
5159
5160 decremented_interesting_task_count: bool,
5161 switch_item: Option<WorkItem>,
5162}
5163
5164impl GuestTask {
5165 fn already_lowered_parameters(&self) -> bool {
5166 self.lower_params.is_none()
5168 }
5169
5170 fn returned_or_cancelled(&self) -> bool {
5171 self.lift_result.is_none()
5173 }
5174
5175 fn ready_to_delete(&self) -> bool {
5176 let threads_completed = self.threads.is_empty();
5177 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5178 let pending_completion_event = matches!(
5179 self.common.event,
5180 Some(Event::Subtask {
5181 status: Status::Returned | Status::ReturnCancelled
5182 })
5183 );
5184 let ready = threads_completed
5185 && !has_sync_result
5186 && !pending_completion_event
5187 && !matches!(self.host_future_state, HostFutureState::Live);
5188 log::trace!(
5189 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5190 threads_completed,
5191 has_sync_result,
5192 pending_completion_event,
5193 self.host_future_state
5194 );
5195 ready
5196 }
5197
5198 fn new(
5199 state: &mut ConcurrentState,
5200 lower_params: RawLower,
5201 lift_result: LiftResult,
5202 caller: Caller,
5203 callback: Option<CallbackFn>,
5204 instance: RuntimeInstance,
5205 async_typed: bool,
5206 async_lifted: bool,
5207 ) -> Result<QualifiedThreadId> {
5208 let host_future_state = match &caller {
5209 Caller::Guest { .. } => HostFutureState::NotApplicable,
5210 Caller::Host {
5211 host_future_present,
5212 ..
5213 } => {
5214 if *host_future_present {
5215 HostFutureState::Live
5216 } else {
5217 HostFutureState::NotApplicable
5218 }
5219 }
5220 };
5221 let task = state.push(Self {
5222 common: WaitableCommon::default(),
5223 lower_params: Some(lower_params),
5224 lift_result: Some(lift_result),
5225 result: None,
5226 callback,
5227 caller,
5228 call_context: CallContext::default(),
5229 sync_result: SyncResult::NotProduced,
5230 cancel_sent: false,
5231 starting_sent: false,
5232 instance,
5233 event: None,
5234 exited: false,
5235 threads: HashSet::new(),
5236 host_future_state,
5237 async_typed,
5238 async_lifted,
5239 decremented_interesting_task_count: false,
5240 switch_item: None,
5241 })?;
5242 let new_thread = GuestThread::new_implicit(state, task)?;
5243 let thread = state.push(new_thread)?;
5244 state.get_mut(task)?.threads.insert(thread);
5245 state.interesting_tasks += 1;
5246 let thread = QualifiedThreadId { task, thread };
5247 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5248 Ok(thread)
5249 }
5250}
5251
5252impl TableDebug for GuestTask {
5253 fn type_name() -> &'static str {
5254 "GuestTask"
5255 }
5256}
5257
5258#[derive(Default)]
5260struct WaitableCommon {
5261 event: Option<Event>,
5263 set: Option<TableId<WaitableSet>>,
5265 handle: Option<u32>,
5267}
5268
5269#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5271enum Waitable {
5272 Host(TableId<HostTask>),
5274 Guest(TableId<GuestTask>),
5276 Transmit(TableId<TransmitHandle>),
5278}
5279
5280impl Waitable {
5281 fn from_instance(
5284 state: Pin<&mut ComponentInstance>,
5285 caller_instance: RuntimeComponentInstanceIndex,
5286 waitable: u32,
5287 ) -> Result<Self> {
5288 use crate::runtime::vm::component::Waitable;
5289
5290 let (waitable, kind) = state.instance_states().0[caller_instance]
5291 .handle_table()
5292 .waitable_rep(waitable)?;
5293
5294 Ok(match kind {
5295 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5296 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5297 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5298 })
5299 }
5300
5301 fn rep(&self) -> u32 {
5303 match self {
5304 Self::Host(id) => id.rep(),
5305 Self::Guest(id) => id.rep(),
5306 Self::Transmit(id) => id.rep(),
5307 }
5308 }
5309
5310 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5314 log::trace!("waitable {self:?} join set {set:?}");
5315
5316 let old = mem::replace(&mut self.common(state)?.set, set);
5317
5318 if let Some(old) = old {
5319 match *self {
5320 Waitable::Host(id) => state.remove_child(id, old),
5321 Waitable::Guest(id) => state.remove_child(id, old),
5322 Waitable::Transmit(id) => state.remove_child(id, old),
5323 }?;
5324
5325 state.get_mut(old)?.ready.remove(self);
5326 }
5327
5328 if let Some(set) = set {
5329 match *self {
5330 Waitable::Host(id) => state.add_child(id, set),
5331 Waitable::Guest(id) => state.add_child(id, set),
5332 Waitable::Transmit(id) => state.add_child(id, set),
5333 }?;
5334
5335 if self.common(state)?.event.is_some() {
5336 self.mark_ready(state)?;
5337 }
5338 }
5339
5340 Ok(())
5341 }
5342
5343 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5345 Ok(match self {
5346 Self::Host(id) => &mut state.get_mut(*id)?.common,
5347 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5348 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5349 })
5350 }
5351
5352 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5358 if self.common(state)?.set.is_some() {
5359 bail!(Trap::WaitableSyncAndAsync);
5360 }
5361 Ok(())
5362 }
5363
5364 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5368 log::trace!("set event for {self:?}: {event:?}");
5369 self.common(state)?.event = event;
5370 self.mark_ready(state)
5371 }
5372
5373 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5375 let common = self.common(state)?;
5376 let event = common.event.take();
5377 if let Some(set) = self.common(state)?.set {
5378 state.get_mut(set)?.ready.remove(self);
5379 }
5380
5381 Ok(event)
5382 }
5383
5384 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5388 if let Some(set) = self.common(state)?.set {
5389 let set_state = state.get_mut(set)?;
5390 set_state.ready.insert(*self);
5391
5392 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5393 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5394 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5395
5396 let item = match mode {
5397 WaitMode::Caller { fiber, callee } => {
5398 let item = WorkItem::ResumeFiber {
5410 instance: state.get_mut(thread.task)?.instance,
5411 thread,
5412 fiber,
5413 };
5414
5415 if let Some(Event::Subtask {
5416 status: Status::Starting,
5417 }) = &self.common(state)?.event
5418 {
5419 state.set_switch_item(item)?;
5423 } else {
5424 if state.get_mut(callee)?.switch_item.is_some() {
5425 bail_bug!(
5426 "`GuestTask::switch_item` is already `Some(_)` when we need \
5427 to deliver a subtask status update to the caller"
5428 );
5429 }
5430 state.get_mut(callee)?.switch_item = Some(item);
5431 }
5432 None
5433 }
5434 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5435 instance: state.get_mut(thread.task)?.instance,
5436 thread,
5437 fiber,
5438 }),
5439 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5440 instance: state.get_mut(thread.task)?.instance,
5441 call: GuestCall {
5442 thread,
5443 kind: GuestCallKind::DeliverEvent {
5444 instance,
5445 set: Some(set),
5446 },
5447 },
5448 }),
5449 };
5450
5451 if let Some(item) = item {
5452 state.push_high_priority(item);
5453 }
5454 }
5455 }
5456 Ok(())
5457 }
5458
5459 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5461 match self {
5462 Self::Host(task) => {
5463 log::trace!("delete host task {task:?}");
5464 state.delete(*task)?;
5465 }
5466 Self::Guest(task) => {
5467 log::trace!("delete guest task {task:?}");
5468 let task = state.delete(*task)?;
5469
5470 debug_assert!(task.decremented_interesting_task_count);
5477 }
5478 Self::Transmit(task) => {
5479 state.delete(*task)?;
5480 }
5481 }
5482
5483 Ok(())
5484 }
5485}
5486
5487impl fmt::Debug for Waitable {
5488 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5489 match self {
5490 Self::Host(id) => write!(f, "{id:?}"),
5491 Self::Guest(id) => write!(f, "{id:?}"),
5492 Self::Transmit(id) => write!(f, "{id:?}"),
5493 }
5494 }
5495}
5496
5497#[derive(Default)]
5499struct WaitableSet {
5500 ready: BTreeSet<Waitable>,
5502 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5504 is_sync_call_set: bool,
5507}
5508
5509impl TableDebug for WaitableSet {
5510 fn type_name() -> &'static str {
5511 "WaitableSet"
5512 }
5513}
5514
5515type RawLower =
5517 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5518
5519type RawLift = Box<
5521 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5522>;
5523
5524type LiftedResult = Box<dyn Any + Send + Sync>;
5528
5529struct DummyResult;
5532
5533#[derive(Default)]
5535pub struct ConcurrentInstanceState {
5536 backpressure: u16,
5538 do_not_enter: bool,
5540 do_not_suspend: bool,
5543 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5546}
5547
5548impl ConcurrentInstanceState {
5549 pub fn pending_is_empty(&self) -> bool {
5550 self.pending.is_empty()
5551 }
5552}
5553
5554#[derive(Debug, Copy, Clone)]
5555pub(crate) enum CurrentThread {
5556 Guest(QualifiedThreadId),
5559 Host(TableId<HostTask>),
5561 GuestTask(TableId<GuestTask>),
5565 None,
5567}
5568
5569impl CurrentThread {
5570 fn guest(&self) -> Option<&QualifiedThreadId> {
5571 match self {
5572 Self::Guest(id) => Some(id),
5573 _ => None,
5574 }
5575 }
5576
5577 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5578 match self {
5579 Self::Guest(id) => Some(id.task),
5580 Self::GuestTask(id) => Some(*id),
5581 _ => None,
5582 }
5583 }
5584
5585 fn host(&self) -> Option<TableId<HostTask>> {
5586 match self {
5587 Self::Host(id) => Some(*id),
5588 _ => None,
5589 }
5590 }
5591
5592 fn is_none(&self) -> bool {
5593 matches!(self, Self::None)
5594 }
5595}
5596
5597impl From<QualifiedThreadId> for CurrentThread {
5598 fn from(id: QualifiedThreadId) -> Self {
5599 Self::Guest(id)
5600 }
5601}
5602
5603impl From<TableId<HostTask>> for CurrentThread {
5604 fn from(id: TableId<HostTask>) -> Self {
5605 Self::Host(id)
5606 }
5607}
5608
5609enum Priority {
5610 Switch,
5611 High,
5612 Low,
5613}
5614
5615pub struct ConcurrentState {
5617 unforced_current_thread: CurrentThread,
5623
5624 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5629 table: AlwaysMut<ResourceTable>,
5631 switch_item: Option<WorkItem>,
5639 high_priority: VecDeque<WorkItem>,
5641 low_priority: VecDeque<WorkItem>,
5643 suspend_reason: Option<SuspendReason>,
5647 worker: Option<StoreFiber<'static>>,
5651 worker_item: Option<WorkerItem>,
5653
5654 global_error_context_ref_counts:
5667 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5668
5669 interesting_tasks: usize,
5682
5683 interesting_tasks_empty_waker: Option<Waker>,
5687
5688 ready_for_concurrent_call_waker: Option<Waker>,
5693
5694 event_loop_running: bool,
5696}
5697
5698impl Default for ConcurrentState {
5699 fn default() -> Self {
5700 Self {
5701 unforced_current_thread: CurrentThread::None,
5702 table: AlwaysMut::new(ResourceTable::new()),
5703 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5704 switch_item: None,
5705 high_priority: VecDeque::new(),
5706 low_priority: VecDeque::new(),
5707 suspend_reason: None,
5708 worker: None,
5709 worker_item: None,
5710 global_error_context_ref_counts: BTreeMap::new(),
5711 interesting_tasks: 0,
5712 interesting_tasks_empty_waker: None,
5713 ready_for_concurrent_call_waker: None,
5714 event_loop_running: false,
5715 }
5716 }
5717}
5718
5719impl ConcurrentState {
5720 pub(crate) fn take_fibers_and_futures(
5737 &mut self,
5738 fibers: &mut Vec<StoreFiber<'static>>,
5739 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5740 ) {
5741 let mut items = Vec::new();
5742 for entry in self.table.get_mut().iter_mut() {
5743 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5744 for mode in mem::take(&mut set.waiting).into_values() {
5745 match mode {
5746 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5747 fibers.push(fiber);
5748 }
5749 WaitMode::Callback(_) => {}
5750 }
5751 }
5752 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5753 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5754 mem::replace(&mut thread.state, GuestThreadState::Completed)
5755 {
5756 fibers.push(fiber);
5757 }
5758 } else if let Some(task) = entry.downcast_mut::<GuestTask>() {
5759 if let Some(item) = task.switch_item.take() {
5760 items.push(item);
5761 }
5762 }
5763 }
5764
5765 if let Some(fiber) = self.worker.take() {
5766 fibers.push(fiber);
5767 }
5768
5769 let mut handle_item = |item| match item {
5770 WorkItem::ResumeFiber { fiber, .. } => {
5771 fibers.push(fiber);
5772 }
5773 WorkItem::PushFuture(future) => {
5774 self.futures
5775 .get_mut()
5776 .as_mut()
5777 .unwrap()
5778 .push(future.into_inner());
5779 }
5780 WorkItem::ResumeThread { .. }
5781 | WorkItem::GuestCall { .. }
5782 | WorkItem::WorkerFunction(_) => {}
5783 };
5784
5785 for item in items {
5786 handle_item(item);
5787 }
5788 if let Some(item) = self.switch_item.take() {
5789 handle_item(item);
5790 }
5791 for item in mem::take(&mut self.high_priority) {
5792 handle_item(item);
5793 }
5794 for item in mem::take(&mut self.low_priority) {
5795 handle_item(item);
5796 }
5797
5798 if let Some(them) = self.futures.get_mut().take() {
5799 futures.push(them);
5800 }
5801 }
5802
5803 #[cfg(feature = "gc")]
5804 pub(crate) fn trace_fiber_roots(
5805 &mut self,
5806 modules: &ModuleRegistry,
5807 unwind: &dyn Unwind,
5808 gc_roots_list: &mut GcRootsList,
5809 ) {
5810 let ConcurrentState {
5811 table,
5812 worker,
5813 switch_item,
5814 high_priority,
5815 low_priority,
5816
5817 futures: _,
5821
5822 worker_item: _,
5824 unforced_current_thread: _,
5825 suspend_reason: _,
5826 global_error_context_ref_counts: _,
5827 interesting_tasks: _,
5828 interesting_tasks_empty_waker: _,
5829 ready_for_concurrent_call_waker: _,
5830 event_loop_running: _,
5831 } = self;
5832
5833 for entry in table.get_mut().iter_mut() {
5834 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5835 for mode in set.waiting.values_mut() {
5836 match mode {
5837 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5838 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5839 }
5840 WaitMode::Callback(_) => {}
5841 }
5842 }
5843 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5844 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5845 &mut thread.state
5846 {
5847 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5848 }
5849 }
5850 }
5851
5852 if let Some(fiber) = worker {
5853 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5854 }
5855
5856 let mut handle_item = |item: &mut WorkItem| match item {
5857 WorkItem::ResumeFiber { fiber, .. } => {
5858 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5859 }
5860 WorkItem::PushFuture(_future) => {
5861 }
5864 WorkItem::ResumeThread { .. }
5865 | WorkItem::GuestCall { .. }
5866 | WorkItem::WorkerFunction(_) => {}
5867 };
5868
5869 if let Some(item) = switch_item {
5870 handle_item(item);
5871 }
5872 for item in high_priority {
5873 handle_item(item);
5874 }
5875 for item in low_priority {
5876 handle_item(item);
5877 }
5878 }
5879
5880 fn push<V: Send + Sync + 'static>(
5881 &mut self,
5882 value: V,
5883 ) -> Result<TableId<V>, ResourceTableError> {
5884 self.table.get_mut().push(value).map(TableId::from)
5885 }
5886
5887 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5888 self.table.get_mut().get_mut(&Resource::from(id))
5889 }
5890
5891 pub fn add_child<T: 'static, U: 'static>(
5892 &mut self,
5893 child: TableId<T>,
5894 parent: TableId<U>,
5895 ) -> Result<(), ResourceTableError> {
5896 self.table
5897 .get_mut()
5898 .add_child(Resource::from(child), Resource::from(parent))
5899 }
5900
5901 pub fn remove_child<T: 'static, U: 'static>(
5902 &mut self,
5903 child: TableId<T>,
5904 parent: TableId<U>,
5905 ) -> Result<(), ResourceTableError> {
5906 self.table
5907 .get_mut()
5908 .remove_child(Resource::from(child), Resource::from(parent))
5909 }
5910
5911 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5912 self.table.get_mut().delete(Resource::from(id))
5913 }
5914
5915 fn push_future(&mut self, future: HostTaskFuture) {
5916 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5923 }
5924
5925 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
5926 log::trace!("set switch item: {item:?}");
5927
5928 if self.switch_item.is_some() {
5929 bail_bug!("switch item already set");
5930 }
5931
5932 self.switch_item = Some(item);
5933
5934 Ok(())
5935 }
5936
5937 fn push_high_priority(&mut self, item: WorkItem) {
5938 log::trace!("push high priority: {item:?}");
5939 self.high_priority.push_front(item);
5940 }
5941
5942 fn push_low_priority(&mut self, item: WorkItem) {
5943 log::trace!("push low priority: {item:?}");
5944 self.low_priority.push_front(item);
5945 }
5946
5947 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
5948 match priority {
5949 Priority::Switch => self.set_switch_item(item)?,
5950 Priority::High => self.push_high_priority(item),
5951 Priority::Low => self.push_low_priority(item),
5952 }
5953
5954 Ok(())
5955 }
5956
5957 fn promote_instance_local_thread_work_item(
5958 &mut self,
5959 current_instance: RuntimeInstance,
5960 ) -> Result<bool> {
5961 log::trace!("promote thread work items for {current_instance:?}");
5962
5963 self.promote_work_item_matching(|item: &WorkItem| {
5964 let result = match item {
5965 WorkItem::ResumeThread { instance, .. }
5966 | WorkItem::ResumeFiber { instance, .. }
5967 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
5968 _ => false,
5969 };
5970
5971 log::trace!("candidate {item:?}: {result}");
5972 result
5973 })
5974 }
5975
5976 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
5977 self.promote_work_item_matching(|item: &WorkItem| match item {
5978 WorkItem::ResumeThread {
5979 thread: item_thread,
5980 ..
5981 }
5982 | WorkItem::GuestCall {
5983 call:
5984 GuestCall {
5985 thread: item_thread,
5986 ..
5987 },
5988 ..
5989 } => *item_thread == thread,
5990 _ => false,
5991 })
5992 }
5993
5994 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
5995 where
5996 F: FnMut(&WorkItem) -> bool,
5997 {
5998 for item in mem::take(&mut self.high_priority).into_iter().rev() {
6003 if self.switch_item.is_none() && predicate(&item) {
6004 self.set_switch_item(item)?;
6005 } else {
6006 self.push_high_priority(item);
6007 }
6008 }
6009
6010 if self.switch_item.is_none() {
6011 for item in mem::take(&mut self.low_priority).into_iter().rev() {
6012 if self.switch_item.is_none() && predicate(&item) {
6013 self.set_switch_item(item)?;
6014 } else {
6015 self.push_low_priority(item);
6016 }
6017 }
6018 }
6019
6020 Ok(self.switch_item.is_some())
6021 }
6022
6023 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
6029 let (task, is_host) = (task >> 1, task & 1 == 1);
6030 if is_host {
6031 let task: TableId<HostTask> = TableId::new(task);
6032 Ok(&mut self.get_mut(task)?.call_context)
6033 } else {
6034 let task: TableId<GuestTask> = TableId::new(task);
6035 Ok(&mut self.get_mut(task)?.call_context)
6036 }
6037 }
6038
6039 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6040 match self.futures.get_mut().as_mut() {
6041 Some(f) => Ok(f),
6042 None => bail_bug!("futures field of concurrent state is currently taken"),
6043 }
6044 }
6045
6046 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6047 self.table.get_mut()
6048 }
6049
6050 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
6052 let task = match cur {
6053 CurrentThread::GuestTask(task) => task,
6054 CurrentThread::Guest(thread) => thread.task,
6055 CurrentThread::Host(id) => {
6056 return Some(CurrentThread::GuestTask(self.get_mut(id).ok()?.caller));
6057 }
6058 CurrentThread::None => return None,
6059 };
6060 let task = self.get_mut(task).ok()?;
6061 Some(match task.caller {
6062 Caller::Host { caller, .. } => caller,
6063 Caller::Guest { thread } => thread.into(),
6064 })
6065 }
6066}
6067
6068fn for_any_lower<
6071 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6072>(
6073 fun: F,
6074) -> F {
6075 fun
6076}
6077
6078fn for_any_lift<
6080 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6081>(
6082 fun: F,
6083) -> F {
6084 fun
6085}
6086
6087fn check_ambient_store(id: StoreId) {
6088 let message = "\
6089 `Future`s which depend on asynchronous component tasks, streams, or \
6090 futures to complete may only be polled from the event loop of the \
6091 store to which they belong. Please use \
6092 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6093 ";
6094 tls::try_get(|store| {
6095 let matched = match store {
6096 tls::TryGet::Some(store) => store.id() == id,
6097 tls::TryGet::Taken | tls::TryGet::None => false,
6098 };
6099
6100 if !matched {
6101 panic!("{message}")
6102 }
6103 });
6104}
6105
6106fn check_recursive_run() {
6109 tls::try_get(|store| {
6110 if !matches!(store, tls::TryGet::None) {
6111 panic!("Recursive `StoreContextMut::run_concurrent` calls not supported")
6112 }
6113 });
6114}
6115
6116fn unpack_callback_code(code: u32) -> (u32, u32) {
6117 (code & 0xF, code >> 4)
6118}
6119
6120struct WaitableCheckParams {
6124 set: TableId<WaitableSet>,
6125 options: OptionsIndex,
6126 payload: u32,
6127}
6128
6129enum WaitableCheck {
6132 Wait,
6133 Poll,
6134}
6135
6136#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
6145pub struct GuestTaskId(TableId<GuestTask>);
6146
6147pub(crate) struct PreparedCall<R> {
6149 handle: Func,
6151 thread: QualifiedThreadId,
6153 param_count: usize,
6155 rx: oneshot::Receiver<LiftedResult>,
6158 runtime_instance: RuntimeInstance,
6160 _phantom: PhantomData<R>,
6161}
6162
6163impl<R> PreparedCall<R> {
6164 pub(crate) fn task_id(&self) -> TaskId {
6166 TaskId {
6167 task: self.thread.task,
6168 runtime_instance: self.runtime_instance,
6169 }
6170 }
6171}
6172
6173pub(crate) struct TaskId {
6175 task: TableId<GuestTask>,
6176 runtime_instance: RuntimeInstance,
6177}
6178
6179impl TaskId {
6180 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6186 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6187 let delete = if !task.already_lowered_parameters() {
6188 store.cancel_guest_subtask_without_lowered_parameters(
6189 self.runtime_instance,
6190 self.task,
6191 )?;
6192 true
6193 } else {
6194 task.host_future_state = HostFutureState::Dropped;
6195 task.ready_to_delete()
6196 };
6197 if delete {
6198 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
6199 }
6200 Ok(())
6201 }
6202}
6203
6204pub(crate) fn prepare_call<T, R>(
6210 mut store: StoreContextMut<T>,
6211 handle: Func,
6212 param_count: usize,
6213 host_future_present: bool,
6214 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6215 + Send
6216 + Sync
6217 + 'static,
6218 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6219 + Send
6220 + Sync
6221 + 'static,
6222) -> Result<PreparedCall<R>> {
6223 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6224
6225 let instance = handle.instance().id().get(store.0);
6226 let options = &instance.component().env_component().options[options];
6227 let ty = &instance.component().types()[ty];
6228 let async_typed = ty.async_;
6229 let async_lifted = raw_options.async_;
6230 let task_return_type = ty.results;
6231 let component_instance = raw_options.instance;
6232 let callback = options.callback.map(|i| instance.runtime_callback(i));
6233 let memory = options
6234 .memory()
6235 .map(|i| instance.runtime_memory(i))
6236 .map(SendSyncPtr::new);
6237 let string_encoding = options.string_encoding;
6238 let token = StoreToken::new(store.as_context_mut());
6239 let caller = store.0.current_thread()?;
6240 let state = store.0.concurrent_state_mut()?;
6241
6242 let (tx, rx) = oneshot::channel();
6243
6244 let instance = handle.instance().runtime_instance(component_instance);
6245 let thread = GuestTask::new(
6246 state,
6247 Box::new(for_any_lower(move |store, params| {
6248 lower_params(token.as_context_mut(store), params)
6249 })),
6250 LiftResult {
6251 lift: Box::new(for_any_lift(move |store, result| {
6252 lift_result(store, result)
6253 })),
6254 ty: task_return_type,
6255 memory,
6256 string_encoding,
6257 },
6258 Caller::Host {
6259 tx: Some(tx),
6260 host_future_present,
6261 caller,
6262 },
6263 callback.map(|callback| {
6264 let callback = SendSyncPtr::new(callback);
6265 let instance = handle.instance();
6266 Box::new(move |store: &mut dyn VMStore, event, handle| {
6267 let store = token.as_context_mut(store);
6268 unsafe { instance.call_callback(store, callback, event, handle) }
6271 }) as CallbackFn
6272 }),
6273 instance,
6274 async_typed,
6275 async_lifted,
6276 )?;
6277
6278 if !store.0.may_enter() {
6279 bail!(Trap::CannotEnterComponent);
6280 }
6281
6282 Ok(PreparedCall {
6283 handle,
6284 thread,
6285 param_count,
6286 runtime_instance: instance,
6287 rx,
6288 _phantom: PhantomData,
6289 })
6290}
6291
6292pub(crate) struct StagedCall<R> {
6293 store: StoreId,
6294 task: TableId<GuestTask>,
6295 rx: oneshot::Receiver<LiftedResult>,
6296 _marker: PhantomData<fn() -> R>,
6297}
6298
6299impl<R> StagedCall<R> {
6300 pub(crate) fn new<T: 'static>(
6307 mut store: StoreContextMut<T>,
6308 prepared: PreparedCall<R>,
6309 ) -> Result<StagedCall<R>> {
6310 let PreparedCall {
6311 handle,
6312 thread,
6313 param_count,
6314 rx,
6315 ..
6316 } = prepared;
6317
6318 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6319
6320 Ok(StagedCall {
6321 store: store.0.id(),
6322 task: thread.task,
6323 rx,
6324 _marker: PhantomData,
6325 })
6326 }
6327
6328 fn task(&self) -> GuestTaskId {
6329 GuestTaskId(self.task)
6330 }
6331}
6332
6333impl<R> Future for StagedCall<R>
6334where
6335 R: 'static,
6336{
6337 type Output = Result<R>;
6338
6339 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6340 check_ambient_store(self.store);
6341 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6342 Ok(r) => match r.downcast() {
6343 Ok(r) => Ok(*r),
6344 Err(_) => bail_bug!("wrong type of value produced"),
6345 },
6346 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6347 })
6348 }
6349}
6350
6351fn stage_call0<T: 'static>(
6354 store: StoreContextMut<T>,
6355 handle: Func,
6356 guest_thread: QualifiedThreadId,
6357 param_count: usize,
6358) -> Result<()> {
6359 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6360 let is_concurrent = raw_options.async_;
6361 let callback = raw_options.callback;
6362 let instance = handle.instance();
6363 let callee = handle.lifted_core_func(store.0);
6364 let post_return = raw_options
6365 .post_return
6366 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6367 let callback = callback.map(|i| {
6368 let instance = instance.id().get(store.0);
6369 SendSyncPtr::new(instance.runtime_callback(i))
6370 });
6371
6372 log::trace!("queueing call {guest_thread:?}");
6373
6374 unsafe {
6378 instance.stage_call(
6379 store,
6380 guest_thread,
6381 SendSyncPtr::new(callee),
6382 param_count,
6383 1,
6384 is_concurrent,
6385 callback,
6386 post_return.map(SendSyncPtr::new),
6387 true,
6388 )
6389 }
6390}