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 let already_running = self
1228 .0
1229 .concurrent_state_mut_already_forced_current_thread()
1230 .event_loop_running;
1231 if already_running {
1232 bail!("Recursive `StoreContextMut::run_concurrent` calls not supported")
1233 }
1234 let token = StoreToken::new(self.as_context_mut());
1235
1236 struct Dropper<'a, T: 'static, V> {
1237 store: StoreContextMut<'a, T>,
1238 value: ManuallyDrop<V>,
1239 }
1240
1241 impl<'a, T, V> Drop for Dropper<'a, T, V> {
1242 fn drop(&mut self) {
1243 self.store
1244 .0
1245 .concurrent_state_mut_already_forced_current_thread()
1246 .event_loop_running = false;
1247
1248 tls::set(self.store.0, || {
1249 unsafe { ManuallyDrop::drop(&mut self.value) }
1254 });
1255 }
1256 }
1257
1258 let accessor = &Accessor::new(token);
1259 self.0
1260 .concurrent_state_mut_already_forced_current_thread()
1261 .event_loop_running = true;
1262 let dropper = &mut Dropper {
1263 store: self,
1264 value: ManuallyDrop::new(fun(accessor)),
1265 };
1266 let future = unsafe { Pin::new_unchecked(dropper.value.deref_mut()) };
1268
1269 dropper
1270 .store
1271 .as_context_mut()
1272 .poll_until(future, trap_on_idle)
1273 .await
1274 }
1275
1276 async fn poll_until<R>(
1282 mut self,
1283 mut future: Pin<&mut impl Future<Output = R>>,
1284 trap_on_idle: bool,
1285 ) -> Result<R> {
1286 struct Reset<'a, T: 'static> {
1287 store: StoreContextMut<'a, T>,
1288 futures: Option<FuturesUnordered<HostTaskFuture>>,
1289 }
1290
1291 impl<'a, T> Drop for Reset<'a, T> {
1292 fn drop(&mut self) {
1293 if let Some(futures) = self.futures.take() {
1294 *self
1295 .store
1296 .0
1297 .concurrent_state_mut_already_forced_current_thread()
1298 .futures
1299 .get_mut() = Some(futures);
1300 }
1301 }
1302 }
1303
1304 loop {
1305 let futures = self.0.concurrent_state_mut()?.futures.get_mut().take();
1309 let mut reset = Reset {
1310 store: self.as_context_mut(),
1311 futures,
1312 };
1313 let mut next = match reset.futures.as_mut() {
1314 Some(f) => pin!(f.next()),
1315 None => bail_bug!("concurrent state missing futures field"),
1316 };
1317
1318 enum PollResult<R> {
1319 Complete(R),
1320 ProcessWork {
1321 ready: Option<WorkItem>,
1322 low_priority: bool,
1323 },
1324 }
1325
1326 let result = future::poll_fn(|cx| {
1327 if let Poll::Ready(value) = tls::set(reset.store.0, || future.as_mut().poll(cx)) {
1330 return Poll::Ready(Ok(PollResult::Complete(value)));
1331 }
1332
1333 let next = match tls::set(reset.store.0, || next.as_mut().poll(cx)) {
1337 Poll::Ready(Some(output)) => {
1338 match output {
1339 Err(e) => return Poll::Ready(Err(e)),
1340 Ok(()) => {}
1341 }
1342 Poll::Ready(true)
1343 }
1344 Poll::Ready(None) => Poll::Ready(false),
1345 Poll::Pending => Poll::Pending,
1346 };
1347
1348 let state = reset.store.0.concurrent_state_mut()?;
1363 let mut ready = state.switch_item.take();
1364 let mut low_priority = false;
1365 if ready.is_none() {
1366 ready = state.high_priority.pop_back();
1367 if ready.is_none() {
1368 ready = state.low_priority.pop_back();
1369 low_priority = true;
1370 }
1371 }
1372 if ready.is_some() {
1373 return Poll::Ready(Ok(PollResult::ProcessWork {
1374 ready,
1375 low_priority,
1376 }));
1377 }
1378
1379 return match next {
1383 Poll::Ready(true) => {
1384 Poll::Ready(Ok(PollResult::ProcessWork {
1390 ready: None,
1391 low_priority: false,
1392 }))
1393 }
1394 Poll::Ready(false) => {
1395 if let Poll::Ready(value) =
1399 tls::set(reset.store.0, || future.as_mut().poll(cx))
1400 {
1401 Poll::Ready(Ok(PollResult::Complete(value)))
1402 } else {
1403 if trap_on_idle {
1409 Poll::Ready(Err(if reset.store.0.any_may_not_suspend()? {
1416 Trap::CannotBlockSyncTask.into()
1417 } else {
1418 Trap::AsyncDeadlock.into()
1420 }))
1421 } else {
1422 Poll::Pending
1426 }
1427 }
1428 }
1429 Poll::Pending => Poll::Pending,
1434 };
1435 })
1436 .await;
1437
1438 drop(reset);
1442
1443 match result? {
1444 PollResult::Complete(value) => break Ok(value),
1447 PollResult::ProcessWork {
1450 ready,
1451 low_priority,
1452 } => {
1453 struct Dispose<'a, T: 'static> {
1454 store: StoreContextMut<'a, T>,
1455 ready: Option<WorkItem>,
1456 }
1457
1458 impl<'a, T> Drop for Dispose<'a, T> {
1459 fn drop(&mut self) {
1460 if let Some(item) = self.ready.take() {
1461 match item {
1462 WorkItem::ResumeFiber { mut fiber, .. } => {
1463 fiber.dispose(self.store.0)
1464 }
1465 WorkItem::PushFuture(future) => {
1466 tls::set(self.store.0, move || drop(future))
1467 }
1468 _ => {}
1469 }
1470 }
1471 }
1472 }
1473
1474 let mut dispose = Dispose {
1475 store: self.as_context_mut(),
1476 ready,
1477 };
1478
1479 if low_priority {
1501 dispose.store.0.yield_now().await
1502 }
1503
1504 if let Some(item) = dispose.ready.take() {
1505 dispose
1506 .store
1507 .as_context_mut()
1508 .handle_work_item(item)
1509 .await?;
1510 }
1511 }
1512 }
1513 }
1514 }
1515
1516 async fn handle_work_item(self, item: WorkItem) -> Result<()> {
1518 log::trace!("handle work item {item:?}");
1519 match item {
1520 WorkItem::PushFuture(future) => {
1521 self.0
1522 .concurrent_state_mut()?
1523 .futures_mut()?
1524 .push(future.into_inner());
1525 }
1526 WorkItem::ResumeFiber { fiber, .. } => {
1527 self.0.resume_fiber(fiber).await?;
1528 }
1529 WorkItem::ResumeThread { thread, .. } => {
1530 if let GuestThreadState::Ready { fiber, .. } = mem::replace(
1531 &mut self.0.concurrent_state_mut()?.get_mut(thread.thread)?.state,
1532 GuestThreadState::Running,
1533 ) {
1534 self.0.resume_fiber(fiber).await?;
1535 } else {
1536 bail_bug!("cannot resume non-pending thread {thread:?}");
1537 }
1538 }
1539 WorkItem::GuestCall { call, .. } => {
1540 if call.is_ready(self.0)? {
1541 self.run_on_worker(WorkerItem::GuestCall(call)).await?;
1542 } else {
1543 let state = self.0.concurrent_state_mut()?;
1544 let task = state.get_mut(call.thread.task)?;
1545 if !task.starting_sent {
1546 task.starting_sent = true;
1547 if let GuestCallKind::StartImplicit(_) = &call.kind {
1548 Waitable::Guest(call.thread.task).set_event(
1549 state,
1550 Some(Event::Subtask {
1551 status: Status::Starting,
1552 }),
1553 )?;
1554 }
1555 }
1556
1557 let instance = state.get_mut(call.thread.task)?.instance;
1558 self.0
1559 .instance_state(instance)
1560 .concurrent_state()
1561 .pending
1562 .insert(call.thread, call.kind);
1563 }
1564 }
1565 WorkItem::WorkerFunction(fun) => {
1566 self.run_on_worker(WorkerItem::Function(fun)).await?;
1567 }
1568 }
1569
1570 Ok(())
1571 }
1572
1573 async fn run_on_worker(self, item: WorkerItem) -> Result<()> {
1575 let worker = if let Some(fiber) = self.0.concurrent_state_mut()?.worker.take() {
1576 fiber
1577 } else {
1578 unsafe {
1597 fiber::make_fiber_unchecked(self.0, move |store| {
1598 loop {
1599 let Some(item) = store.concurrent_state_mut()?.worker_item.take() else {
1600 bail_bug!("worker_item not present when resuming fiber")
1601 };
1602 match item {
1603 WorkerItem::GuestCall(call) => handle_guest_call(store, call)?,
1604 WorkerItem::Function(fun) => fun.into_inner()(store)?,
1605 }
1606
1607 store.suspend(SuspendReason::NeedWork)?;
1608 }
1609 })?
1610 }
1611 };
1612
1613 let worker_item = &mut self.0.concurrent_state_mut()?.worker_item;
1614 assert!(worker_item.is_none());
1615 *worker_item = Some(item);
1616
1617 self.0.resume_fiber(worker).await
1618 }
1619
1620 pub(crate) fn wrap_call<F, R>(self, closure: F) -> impl Future<Output = Result<R>> + 'static
1625 where
1626 T: 'static,
1627 F: FnOnce(&Accessor<T>) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
1628 + Send
1629 + Sync
1630 + 'static,
1631 R: Send + Sync + 'static,
1632 {
1633 let token = StoreToken::new(self);
1634 async move {
1635 let mut accessor = Accessor::new(token);
1636 closure(&mut accessor).await
1637 }
1638 }
1639
1640 pub fn async_call_stack(&mut self) -> Result<impl Iterator<Item = GuestTaskId>> {
1662 let mut cur = Some(self.0.current_thread()?);
1663 let state = self.0.concurrent_state_mut()?;
1664 Ok(core::iter::from_fn(move || {
1665 while let Some(t) = cur {
1666 cur = state.parent(t);
1667 if let Some(task) = t.guest_task() {
1668 return Some(GuestTaskId(task));
1669 }
1670 }
1671
1672 None
1673 }))
1674 }
1675
1676 pub(crate) async fn start_instance(
1677 &mut self,
1678 instance: ModuleInstance,
1679 ) -> Result<ModuleInstance> {
1680 let (tx, rx) = oneshot::channel();
1681 let token = StoreToken::new(self.as_context_mut());
1682 self.0.queue_task(move |store| {
1683 _ = tx.send(
1684 instance
1685 .start_raw(&mut token.as_context_mut(store))
1686 .map(|()| instance),
1687 );
1688 Ok(())
1689 })?;
1690 self.as_context_mut()
1691 .run_concurrent_trap_on_idle(async |_| {
1692 rx.await
1693 .map_err(|_| format_err!("oneshot channel canceled"))
1694 })
1695 .await??
1696 }
1697}
1698
1699pub type EnteredHostTask = Option<(TableId<HostTask>, QualifiedThreadId)>;
1705
1706impl StoreOpaque {
1707 #[inline]
1710 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1711 if !self.concurrency_support() {
1713 return Ok(CurrentThread::None);
1714 }
1715
1716 if !self
1719 .vm_store_context_mut()
1720 .current_thread_mut()
1721 .is_deferred()
1722 {
1723 return Ok(self
1724 .concurrent_state_mut_already_forced_current_thread()
1725 .unforced_current_thread);
1726 }
1727
1728 self.force_deferred_current_thread()
1729 }
1730
1731 #[cold]
1734 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1735 let state = self.concurrent_state_mut_without_forcing_current_thread();
1744 let id = match state.unforced_current_thread.guest_task() {
1745 Some(task) => state.get_mut(task)?.instance.instance,
1746 None => bail_bug!("deferred component-model thread with non-guest base"),
1747 };
1748
1749 let mut frames = Vec::new();
1752 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1753 while let Some(ptr) = cur.as_deferred() {
1754 let deferred = unsafe { ptr.as_non_null().as_ref() };
1759 frames.push((
1760 deferred.callee_async != 0,
1761 deferred.callee_instance,
1762 deferred.saved_context,
1763 ));
1764 cur = deferred.parent;
1765 }
1766
1767 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1771
1772 let current_context = *self.vm_store_context_mut().component_context_mut();
1775
1776 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1780 *self.vm_store_context_mut().component_context_mut() = saved_context;
1784 let callee = RuntimeInstance {
1785 instance: id,
1786 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1787 };
1788 self.enter_guest_sync_call(callee_async, callee)?;
1789 }
1790
1791 *self.vm_store_context_mut().component_context_mut() = current_context;
1793
1794 Ok(self
1795 .concurrent_state_mut_without_forcing_current_thread()
1796 .unforced_current_thread)
1797 }
1798
1799 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1800 match self.current_thread()?.guest() {
1801 Some(id) => Ok(*id),
1802 None => bail_bug!("current thread is not a guest thread"),
1803 }
1804 }
1805
1806 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1807 match self.current_thread()?.host() {
1808 Some(id) => Ok(id),
1809 None => bail_bug!("current thread is not a host thread"),
1810 }
1811 }
1812
1813 fn take_pending_cancellation(&mut self) -> Result<bool> {
1816 let thread = self.current_guest_thread()?;
1817 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1818 if let Some(Event::Cancelled) = task.event {
1819 task.event.take();
1820 return Ok(true);
1821 }
1822 Ok(false)
1823 }
1824
1825 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1826 log::trace!("enter sync-typed call {callee:?}");
1827 let state = self.instance_state(callee).concurrent_state();
1828 let old_do_not_suspend = state.do_not_suspend;
1829 state.do_not_suspend = true;
1830
1831 let thread = self.current_guest_thread()?;
1832 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1833 if thread.old_do_not_suspend.is_some() {
1834 bail_bug!("current thread already has `old_do_not_suspend` value");
1835 }
1836
1837 thread.old_do_not_suspend = Some(old_do_not_suspend);
1838
1839 Ok(())
1840 }
1841
1842 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1843 log::trace!("exit sync-typed call {callee:?}");
1844 let thread = self.current_guest_thread()?;
1845 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1846 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1847 bail_bug!("current thread missing `old_do_not_suspend` value");
1848 };
1849 let state = self.instance_state(callee).concurrent_state();
1850 state.do_not_suspend = old_do_not_suspend;
1851 Ok(())
1852 }
1853
1854 pub(crate) fn enter_guest_sync_call(
1866 &mut self,
1867 callee_async_typed: bool,
1868 callee: RuntimeInstance,
1869 ) -> Result<()> {
1870 log::trace!("enter sync-lifted call {callee:?}");
1871 if !self.concurrency_support() {
1872 return self.enter_call_not_concurrent();
1873 }
1874
1875 let thread = self.current_thread()?;
1876 let state = self.concurrent_state_mut()?;
1877 let guest_thread = GuestTask::new(
1878 state,
1879 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1880 LiftResult {
1881 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1882 ty: TypeTupleIndex::reserved_value(),
1883 memory: None,
1884 string_encoding: StringEncoding::Utf8,
1885 },
1886 if let Some(thread) = thread.guest() {
1887 Caller::Guest { thread: *thread }
1888 } else {
1889 Caller::Host {
1890 tx: None,
1891 host_future_present: false,
1892 caller: thread,
1893 }
1894 },
1895 None,
1896 callee,
1897 callee_async_typed,
1898 true,
1899 )?;
1900
1901 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1902 guest_thread.thread,
1903 self,
1904 callee.index,
1905 )?;
1906 self.set_thread(guest_thread)?;
1907
1908 if !callee_async_typed {
1909 self.enter_sync_call(callee)?;
1910 }
1911
1912 Ok(())
1913 }
1914
1915 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1923 if !self.concurrency_support() {
1924 return Ok(self.exit_call_not_concurrent());
1925 }
1926
1927 let thread = match self.current_thread()?.guest() {
1928 Some(t) => *t,
1929 None => bail_bug!("expected task when exiting"),
1930 };
1931 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1932 let instance = task.instance;
1933
1934 let caller = match &task.caller {
1935 &Caller::Guest { thread } => thread.into(),
1936 &Caller::Host { caller, .. } => caller,
1937 };
1938 task.lift_result = None;
1939 task.exited = true;
1940 let async_typed = task.async_typed;
1941
1942 if !async_typed {
1943 self.exit_sync_call(instance)?;
1944 }
1945
1946 self.set_thread(caller)?;
1947
1948 log::trace!("exit sync-lifted call {instance:?}");
1949
1950 if async_typed {
1951 self.switch_or_trap_if_may_not_suspend(instance)?;
1956 }
1957
1958 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1959
1960 Ok(())
1961 }
1962
1963 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
1971 if !self.concurrency_support() {
1972 self.enter_call_not_concurrent()?;
1973 return Ok(None);
1974 }
1975 let caller = self.current_guest_thread()?;
1976 let state = self.concurrent_state_mut()?;
1977 let task = state.push(HostTask::new(caller.task, HostTaskState::CalleeStarted))?;
1978 log::trace!("new host task {task:?}");
1979 self.set_thread(task)?;
1980 Ok(Some((task, caller)))
1981 }
1982
1983 pub(crate) fn host_task_delete(&mut self, task: EnteredHostTask) -> Result<()> {
1990 match task {
1991 Some((task, caller)) => {
1992 self.set_thread(caller)?;
1993 log::trace!("delete host task {task:?}");
1994 self.concurrent_state_mut()?.delete(task)?;
1995 }
1996 None => {
1997 self.exit_call_not_concurrent();
1998 }
1999 }
2000 Ok(())
2001 }
2002
2003 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2006 self.component_instance_mut(instance.instance)
2007 .instance_state(instance.index)
2008 }
2009
2010 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2016 let thread = thread.into();
2017 let state = self.concurrent_state_mut()?;
2018 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2019
2020 if let Some(old_thread) = old_thread.guest() {
2028 let old_context = *self.vm_store_context_mut().component_context_mut();
2029 self.concurrent_state_mut()?
2030 .get_mut(old_thread.thread)?
2031 .context = old_context;
2032 }
2033 if cfg!(debug_assertions) {
2034 *self.vm_store_context_mut().component_context_mut() =
2035 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2036 }
2037 if let Some(thread) = thread.guest() {
2038 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2039 let context = thread.context;
2040 if cfg!(debug_assertions) {
2041 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2042 }
2043 *self.vm_store_context_mut().component_context_mut() = context;
2044 }
2045
2046 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2048 VMLazyThread::none()
2049 } else {
2050 VMLazyThread::forced()
2051 };
2052
2053 Ok(old_thread)
2054 }
2055
2056 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2058 if self.switch_if_may_not_suspend(instance)? {
2059 Ok(())
2060 } else {
2061 Err(Trap::CannotBlockSyncTask.into())
2062 }
2063 }
2064
2065 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2069 self.concurrent_state_mut()?;
2073
2074 Ok(!self.concurrency_support()
2075 || !self
2076 .instance_state(instance)
2077 .concurrent_state()
2078 .do_not_suspend
2079 || self
2080 .concurrent_state_mut()?
2081 .promote_instance_local_thread_work_item(instance)?)
2082 }
2083
2084 fn enter_instance(&mut self, instance: RuntimeInstance) {
2088 log::trace!("enter {instance:?}");
2089 self.instance_state(instance)
2090 .concurrent_state()
2091 .do_not_enter = true;
2092 }
2093
2094 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2098 log::trace!("exit {instance:?}");
2099 self.instance_state(instance)
2100 .concurrent_state()
2101 .do_not_enter = false;
2102 self.partition_pending(instance)
2103 }
2104
2105 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2113 for (thread, kind) in
2114 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2115 {
2116 let call = GuestCall { thread, kind };
2117 if call.is_ready(self)? {
2118 self.concurrent_state_mut()?
2119 .push_high_priority(WorkItem::GuestCall { instance, call });
2120 } else {
2121 self.instance_state(instance)
2122 .concurrent_state()
2123 .pending
2124 .insert(call.thread, call.kind);
2125 }
2126 }
2127
2128 if let Some(waker) = self
2129 .concurrent_state_mut()?
2130 .ready_for_concurrent_call_waker
2131 .take()
2132 {
2133 waker.wake();
2134 }
2135
2136 Ok(())
2137 }
2138
2139 pub(crate) fn backpressure_modify(
2141 &mut self,
2142 caller_instance: RuntimeInstance,
2143 modify: impl FnOnce(u16) -> Option<u16>,
2144 ) -> Result<()> {
2145 let state = self.instance_state(caller_instance).concurrent_state();
2146 let old = state.backpressure;
2147 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2148 state.backpressure = new;
2149
2150 if old > 0 && new == 0 {
2151 self.partition_pending(caller_instance)?;
2154 }
2155
2156 Ok(())
2157 }
2158
2159 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2162 let old_thread = self.current_thread()?;
2163 log::trace!("resume_fiber: save current thread {old_thread:?}");
2164
2165 let fiber = fiber::resolve_or_release(self, fiber).await?;
2166
2167 self.set_thread(old_thread)?;
2168
2169 let state = self.concurrent_state_mut()?;
2170
2171 if let Some(ot) = old_thread.guest() {
2172 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2173 }
2174 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2175
2176 if let Some(mut fiber) = fiber {
2177 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2178 let reason = match state.suspend_reason.take() {
2180 Some(r) => r,
2181 None => bail_bug!("suspend reason missing when resuming fiber"),
2182 };
2183 match reason {
2184 SuspendReason::NeedWork => {
2185 if state.worker.is_none() {
2186 state.worker = Some(fiber);
2187 } else {
2188 fiber.dispose(self);
2189 }
2190 }
2191 SuspendReason::Yielding {
2192 thread,
2193 cancellable,
2194 } => {
2195 state.get_mut(thread.thread)?.state =
2196 GuestThreadState::Ready { fiber, cancellable };
2197 let instance = state.get_mut(thread.task)?.instance;
2198 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2199 }
2200 SuspendReason::ExplicitlySuspending { thread } => {
2201 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2202 }
2203 SuspendReason::Waiting { set, thread } => {
2204 let old = state
2205 .get_mut(set)?
2206 .waiting
2207 .insert(thread, WaitMode::Fiber(fiber));
2208 assert!(old.is_none());
2209 }
2210 SuspendReason::WaitingForGuestSubtask { caller, callee } => {
2211 let set = state.get_mut(caller.thread)?.sync_call_set;
2212 let old = state
2213 .get_mut(set)?
2214 .waiting
2215 .insert(caller, WaitMode::Caller { fiber, callee });
2216 assert!(old.is_none());
2217 }
2218 };
2219 } else {
2220 log::trace!("resume_fiber: fiber has exited");
2221 }
2222
2223 Ok(())
2224 }
2225
2226 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2232 log::trace!("suspend fiber: {reason:?}");
2233
2234 let task = match &reason {
2238 SuspendReason::Yielding { thread, .. }
2239 | SuspendReason::Waiting { thread, .. }
2240 | SuspendReason::WaitingForGuestSubtask { caller: thread, .. }
2241 | SuspendReason::ExplicitlySuspending { thread } => Some(thread.task),
2242 SuspendReason::NeedWork => None,
2243 };
2244
2245 let old_guest_thread = if let Some(task) = task {
2246 let state = self.concurrent_state_mut()?;
2252 if state.switch_item.is_none() {
2253 if let Some(item) = state.get_mut(task)?.switch_item.take() {
2254 state.set_switch_item(item)?;
2255 }
2256 }
2257
2258 self.current_thread()?
2259 } else {
2260 CurrentThread::None
2261 };
2262
2263 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2264 assert!(suspend_reason.is_none());
2265 *suspend_reason = Some(reason);
2266
2267 if !self.fiber_async_state_mut().can_block() {
2270 return Err(format_err!("future dropped"));
2271 }
2272
2273 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2274
2275 if task.is_some() {
2276 self.set_thread(old_guest_thread)?;
2277 }
2278
2279 Ok(())
2280 }
2281
2282 fn wait_for_event(
2283 &mut self,
2284 caller_instance: RuntimeInstance,
2285 waitable: Waitable,
2286 reason: WaitReason,
2287 ) -> Result<()> {
2288 let caller = self.current_guest_thread()?;
2289 let state = self.concurrent_state_mut()?;
2290
2291 waitable.trap_if_in_waitable_set(state)?;
2292
2293 let set = state.get_mut(caller.thread)?.sync_call_set;
2294 waitable.join(state, Some(set))?;
2295
2296 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2297
2298 self.suspend(match reason {
2299 WaitReason::GuestSubtask(callee) => {
2300 SuspendReason::WaitingForGuestSubtask { caller, callee }
2301 }
2302 WaitReason::Other => SuspendReason::Waiting {
2303 set,
2304 thread: caller,
2305 },
2306 })?;
2307 let state = self.concurrent_state_mut()?;
2308 waitable.join(state, None)
2309 }
2310
2311 fn cleanup_thread(
2333 &mut self,
2334 guest_thread: QualifiedThreadId,
2335 runtime_instance: RuntimeInstance,
2336 cleanup_task: CleanupTask,
2337 ) -> Result<()> {
2338 let state = self.concurrent_state_mut()?;
2339 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2342 state.set_switch_item(item)?;
2343 }
2344 let thread_data = state.get_mut(guest_thread.thread)?;
2345 let sync_call_set = thread_data.sync_call_set;
2346 if let Some(guest_id) = thread_data.instance_rep {
2347 self.instance_state(runtime_instance)
2348 .thread_handle_table()
2349 .guest_thread_remove(guest_id)?;
2350 }
2351 let state = self.concurrent_state_mut()?;
2352
2353 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2355 if let Some(Event::Subtask {
2356 status: Status::Returned | Status::ReturnCancelled,
2357 }) = waitable.common(state)?.event
2358 {
2359 waitable.delete_from(state)?;
2360 }
2361 }
2362
2363 state.delete(guest_thread.thread)?;
2364 state.delete(sync_call_set)?;
2365 let task = state.get_mut(guest_thread.task)?;
2366 task.threads.remove(&guest_thread.thread);
2367
2368 if task.threads.is_empty() && !task.returned_or_cancelled() {
2369 bail!(Trap::NoAsyncResult);
2370 }
2371 let ready_to_delete = task.ready_to_delete();
2372
2373 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2374 task.decremented_interesting_task_count = true;
2375
2376 debug_assert!(state.interesting_tasks > 0);
2377 state.interesting_tasks -= 1;
2378 if state.interesting_tasks == 0
2379 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2380 {
2381 waker.wake();
2382 }
2383 }
2384
2385 match cleanup_task {
2386 CleanupTask::Yes => {
2387 if ready_to_delete {
2388 Waitable::Guest(guest_thread.task).delete_from(state)?;
2389 }
2390 }
2391 CleanupTask::No => {}
2392 }
2393
2394 Ok(())
2395 }
2396
2397 fn cancel_guest_subtask_without_lowered_parameters(
2410 &mut self,
2411 caller_instance: RuntimeInstance,
2412 guest_task: TableId<GuestTask>,
2413 ) -> Result<()> {
2414 let concurrent_state = self.concurrent_state_mut()?;
2415 let task = concurrent_state.get_mut(guest_task)?;
2416 assert!(!task.already_lowered_parameters());
2417 task.lower_params = None;
2421 task.lift_result = None;
2422 task.exited = true;
2423 let instance = task.instance;
2424
2425 assert_eq!(1, task.threads.len());
2428 let thread = *task.threads.iter().next().unwrap();
2429 self.cleanup_thread(
2430 QualifiedThreadId {
2431 task: guest_task,
2432 thread,
2433 },
2434 caller_instance,
2435 CleanupTask::No,
2436 )?;
2437
2438 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2440 let pending_count = pending.len();
2441 pending.retain(|thread, _| thread.task != guest_task);
2442 if pending.len() == pending_count {
2444 bail!(Trap::SubtaskCancelAfterTerminal);
2445 }
2446 Ok(())
2447 }
2448
2449 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2452 if !self.concurrency_support() {
2453 return self.current_scope_id_not_concurrent();
2454 }
2455 let (bits, is_host) = match self.current_thread()? {
2456 CurrentThread::Guest(id) => (id.task.rep(), false),
2457 CurrentThread::GuestTask(id) => (id.rep(), false),
2458 CurrentThread::Host(id) => (id.rep(), true),
2459 CurrentThread::None => return Ok(None),
2460 };
2461 assert_eq!((bits << 1) >> 1, bits);
2462 Ok(Some((bits << 1) | u32::from(is_host)))
2463 }
2464
2465 fn queue_task(
2466 &mut self,
2467 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2468 ) -> Result<()> {
2469 self.concurrent_state_mut()?
2470 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2471 Ok(())
2472 }
2473
2474 fn any_may_not_suspend(&mut self) -> Result<bool> {
2483 Ok(self
2491 .concurrent_state_mut()?
2492 .table
2493 .get_mut()
2494 .iter_mut()
2495 .filter_map(|entry| {
2496 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2497 Some(task.instance)
2498 } else {
2499 None
2500 }
2501 })
2502 .collect::<Vec<_>>()
2503 .into_iter()
2504 .any(|instance| {
2505 self.instance_state(instance)
2506 .concurrent_state()
2507 .do_not_suspend
2508 }))
2509 }
2510}
2511
2512enum CleanupTask {
2513 Yes,
2514 No,
2515}
2516
2517impl Instance {
2518 fn get_event(
2521 self,
2522 store: &mut StoreOpaque,
2523 guest_task: TableId<GuestTask>,
2524 set: Option<TableId<WaitableSet>>,
2525 cancellable: bool,
2526 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2527 let state = store.concurrent_state_mut()?;
2528
2529 let event = &mut state.get_mut(guest_task)?.event;
2530 if let Some(ev) = event
2531 && (cancellable || !matches!(ev, Event::Cancelled))
2532 {
2533 log::trace!("deliver event {ev:?} to {guest_task:?}");
2534 let ev = *ev;
2535 *event = None;
2536 return Ok(Some((ev, None)));
2537 }
2538
2539 let set = match set {
2540 Some(set) => set,
2541 None => return Ok(None),
2542 };
2543 let waitable = match state.get_mut(set)?.ready.pop_first() {
2544 Some(v) => v,
2545 None => return Ok(None),
2546 };
2547
2548 let common = waitable.common(state)?;
2549 let handle = match common.handle {
2550 Some(h) => h,
2551 None => bail_bug!("handle not set when delivering event"),
2552 };
2553 let event = match common.event.take() {
2554 Some(e) => e,
2555 None => bail_bug!("event not set when delivering event"),
2556 };
2557
2558 log::trace!(
2559 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2560 );
2561
2562 waitable.on_delivery(store, self, event)?;
2563
2564 Ok(Some((event, Some((waitable, handle)))))
2565 }
2566
2567 fn handle_callback_code(
2573 self,
2574 store: &mut StoreOpaque,
2575 guest_thread: QualifiedThreadId,
2576 runtime_instance: RuntimeComponentInstanceIndex,
2577 code: u32,
2578 ) -> Result<()> {
2579 let (code, set) = unpack_callback_code(code);
2580
2581 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2582
2583 let state = store.concurrent_state_mut()?;
2584
2585 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2586 state.set_switch_item(item)?;
2587 }
2588
2589 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2590 let set = store
2591 .instance_state(self.runtime_instance(runtime_instance))
2592 .handle_table()
2593 .waitable_set_rep(handle)?;
2594
2595 Ok(TableId::<WaitableSet>::new(set))
2596 };
2597
2598 match code {
2599 callback_code::EXIT => {
2600 log::trace!("implicit thread {guest_thread:?} completed");
2601 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2602 task.exited = true;
2603 task.callback = None;
2604
2605 let runtime_instance = self.runtime_instance(runtime_instance);
2606
2607 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2612
2613 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2614 }
2615 callback_code::YIELD => {
2616 let task = state.get_mut(guest_thread.task)?;
2617 if let Some(event) = task.event {
2622 assert!(matches!(event, Event::None | Event::Cancelled));
2623 } else {
2624 task.event = Some(Event::None);
2625 }
2626 let call = GuestCall {
2627 thread: guest_thread,
2628 kind: GuestCallKind::DeliverEvent {
2629 instance: self,
2630 set: None,
2631 },
2632 };
2633 state.push_low_priority(WorkItem::GuestCall {
2636 instance: self.runtime_instance(runtime_instance),
2637 call,
2638 });
2639 }
2640 callback_code::WAIT => {
2641 let set = get_set(store, set)?;
2642 let state = store.concurrent_state_mut()?;
2643
2644 if state.get_mut(guest_thread.task)?.event.is_some()
2645 || !state.get_mut(set)?.ready.is_empty()
2646 {
2647 state.push_high_priority(WorkItem::GuestCall {
2649 instance: self.runtime_instance(runtime_instance),
2650 call: GuestCall {
2651 thread: guest_thread,
2652 kind: GuestCallKind::DeliverEvent {
2653 instance: self,
2654 set: Some(set),
2655 },
2656 },
2657 });
2658 } else {
2659 let old = state
2667 .get_mut(guest_thread.thread)?
2668 .wake_on_cancel
2669 .replace(set);
2670 if !old.is_none() {
2671 bail_bug!("thread unexpectedly had wake_on_cancel set");
2672 }
2673 let old = state
2674 .get_mut(set)?
2675 .waiting
2676 .insert(guest_thread, WaitMode::Callback(self));
2677 if !old.is_none() {
2678 bail_bug!("set's waiting set already had this thread registered");
2679 }
2680 }
2681 }
2682 _ => bail!(Trap::UnsupportedCallbackCode),
2683 }
2684
2685 Ok(())
2686 }
2687
2688 unsafe fn stage_call<T: 'static>(
2695 self,
2696 mut store: StoreContextMut<T>,
2697 guest_thread: QualifiedThreadId,
2698 callee: SendSyncPtr<VMFuncRef>,
2699 param_count: usize,
2700 result_count: usize,
2701 async_: bool,
2702 callback: Option<SendSyncPtr<VMFuncRef>>,
2703 post_return: Option<SendSyncPtr<VMFuncRef>>,
2704 host_caller: bool,
2705 ) -> Result<()> {
2706 unsafe fn make_call<T: 'static>(
2721 store: StoreContextMut<T>,
2722 guest_thread: QualifiedThreadId,
2723 callee: SendSyncPtr<VMFuncRef>,
2724 param_count: usize,
2725 result_count: usize,
2726 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2727 + Send
2728 + Sync
2729 + 'static
2730 + use<T> {
2731 let token = StoreToken::new(store);
2732 move |store: &mut dyn VMStore| {
2733 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2734
2735 store
2736 .concurrent_state_mut()?
2737 .get_mut(guest_thread.thread)?
2738 .state = GuestThreadState::Running;
2739 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2740 let lower = match task.lower_params.take() {
2741 Some(l) => l,
2742 None => bail_bug!("lower_params missing"),
2743 };
2744
2745 lower(store, &mut storage[..param_count])?;
2746
2747 let mut store = token.as_context_mut(store);
2748
2749 unsafe {
2752 crate::Func::call_unchecked_raw(
2753 &mut store,
2754 callee.as_non_null(),
2755 NonNull::new(
2756 &mut storage[..param_count.max(result_count)]
2757 as *mut [MaybeUninit<ValRaw>] as _,
2758 )
2759 .unwrap(),
2760 )?;
2761 }
2762
2763 Ok(storage)
2764 }
2765 }
2766
2767 let call = unsafe {
2771 make_call(
2772 store.as_context_mut(),
2773 guest_thread,
2774 callee,
2775 param_count,
2776 result_count,
2777 )
2778 };
2779
2780 let callee_instance = store
2781 .0
2782 .concurrent_state_mut()?
2783 .get_mut(guest_thread.task)?
2784 .instance;
2785
2786 let fun = if callback.is_some() {
2787 assert!(async_);
2788
2789 Box::new(move |store: &mut dyn VMStore| {
2790 self.add_guest_thread_to_instance_table(
2791 guest_thread.thread,
2792 store,
2793 callee_instance.index,
2794 )?;
2795 let old_thread = store.set_thread(guest_thread)?;
2796 log::trace!(
2797 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2798 );
2799
2800 store.enter_instance(callee_instance);
2801
2802 let storage = call(store)?;
2809
2810 store.exit_instance(callee_instance)?;
2811
2812 store.set_thread(old_thread)?;
2813 let state = store.concurrent_state_mut()?;
2814 if let Some(t) = old_thread.guest() {
2815 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2816 }
2817 log::trace!("stackless call: restored {old_thread:?} as current thread");
2818
2819 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2822
2823 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2824 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2825 } else {
2826 let token = StoreToken::new(store.as_context_mut());
2827 Box::new(move |store: &mut dyn VMStore| {
2828 self.add_guest_thread_to_instance_table(
2829 guest_thread.thread,
2830 store,
2831 callee_instance.index,
2832 )?;
2833 let old_thread = store.set_thread(guest_thread)?;
2834 log::trace!(
2835 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2836 );
2837 let flags = self.id().get(store).instance_flags(callee_instance.index);
2838
2839 let callee_async_typed = store
2840 .concurrent_state_mut()?
2841 .get_mut(guest_thread.task)?
2842 .async_typed;
2843
2844 if !async_ && callee_async_typed {
2848 store.enter_instance(callee_instance);
2849 }
2850
2851 if !callee_async_typed {
2852 store.enter_sync_call(callee_instance)?;
2853 }
2854
2855 let storage = call(store)?;
2862
2863 if !callee_async_typed {
2864 store.exit_sync_call(callee_instance)?;
2865 }
2866
2867 if !async_ {
2868 if callee_async_typed {
2874 store.exit_instance(callee_instance)?;
2875 }
2876
2877 let lift = {
2878 let state = store.concurrent_state_mut()?;
2879 if !state.get_mut(guest_thread.task)?.result.is_none() {
2880 bail_bug!("task has already produced a result");
2881 }
2882
2883 match state.get_mut(guest_thread.task)?.lift_result.take() {
2884 Some(lift) => lift,
2885 None => bail_bug!("lift_result field is missing"),
2886 }
2887 };
2888
2889 let result = (lift.lift)(store, unsafe {
2892 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2893 &storage[..result_count],
2894 )
2895 })?;
2896
2897 let post_return_arg = match result_count {
2898 0 => ValRaw::i32(0),
2899 1 => unsafe { storage[0].assume_init() },
2902 _ => unreachable!(),
2903 };
2904
2905 unsafe {
2906 call_post_return(
2907 token.as_context_mut(store),
2908 post_return.map(|v| v.as_non_null()),
2909 post_return_arg,
2910 flags,
2911 )?;
2912 }
2913
2914 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2915 }
2916
2917 store.set_thread(old_thread)?;
2918
2919 store
2920 .concurrent_state_mut()?
2921 .get_mut(guest_thread.task)?
2922 .exited = true;
2923
2924 log::trace!(
2925 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
2926 );
2927
2928 if callee_async_typed {
2929 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
2934 }
2935
2936 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2938 Ok(())
2939 })
2940 };
2941
2942 store.0.concurrent_state_mut()?.push_work_item(
2943 WorkItem::GuestCall {
2944 instance: callee_instance,
2945 call: GuestCall {
2946 thread: guest_thread,
2947 kind: GuestCallKind::StartImplicit(fun),
2948 },
2949 },
2950 if host_caller {
2951 Priority::High
2952 } else {
2953 Priority::Switch
2954 },
2955 )?;
2956
2957 Ok(())
2958 }
2959
2960 unsafe fn prepare_call<T: 'static>(
2973 self,
2974 mut store: StoreContextMut<T>,
2975 start: NonNull<VMFuncRef>,
2976 return_: NonNull<VMFuncRef>,
2977 caller_instance: RuntimeComponentInstanceIndex,
2978 callee_instance: RuntimeComponentInstanceIndex,
2979 task_return_type: TypeTupleIndex,
2980 callee_async_typed: bool,
2981 memory: *mut VMMemoryDefinition,
2982 string_encoding: StringEncoding,
2983 caller_info: CallerInfo,
2984 ) -> Result<()> {
2985 enum ResultInfo {
2986 Heap { results: u32 },
2987 Stack { result_count: u32 },
2988 }
2989
2990 let result_info = match &caller_info {
2991 CallerInfo::Async {
2992 has_result: true,
2993 params,
2994 } => ResultInfo::Heap {
2995 results: match params.last() {
2996 Some(r) => r.get_u32(),
2997 None => bail_bug!("retptr missing"),
2998 },
2999 },
3000 CallerInfo::Async {
3001 has_result: false, ..
3002 } => ResultInfo::Stack { result_count: 0 },
3003 CallerInfo::Sync {
3004 result_count,
3005 params,
3006 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3007 results: match params.last() {
3008 Some(r) => r.get_u32(),
3009 None => bail_bug!("arg ptr missing"),
3010 },
3011 },
3012 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3013 result_count: *result_count,
3014 },
3015 };
3016
3017 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3018
3019 let start = SendSyncPtr::new(start);
3023 let return_ = SendSyncPtr::new(return_);
3024 let token = StoreToken::new(store.as_context_mut());
3025 let old_thread = store.0.current_guest_thread()?;
3026 let state = store.0.concurrent_state_mut()?;
3027
3028 debug_assert_eq!(
3029 state.get_mut(old_thread.task)?.instance,
3030 self.runtime_instance(caller_instance)
3031 );
3032
3033 let guest_thread = GuestTask::new(
3034 state,
3035 Box::new(move |store, dst| {
3036 let mut store = token.as_context_mut(store);
3037 assert!(dst.len() <= MAX_FLAT_PARAMS);
3038 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3040 let count = match caller_info {
3041 CallerInfo::Async { params, has_result } => {
3045 let params = ¶ms[..params.len() - usize::from(has_result)];
3046 for (param, src) in params.iter().zip(&mut src) {
3047 src.write(*param);
3048 }
3049 params.len()
3050 }
3051
3052 CallerInfo::Sync { params, .. } => {
3054 for (param, src) in params.iter().zip(&mut src) {
3055 src.write(*param);
3056 }
3057 params.len()
3058 }
3059 };
3060 unsafe {
3067 crate::Func::call_unchecked_raw(
3068 &mut store,
3069 start.as_non_null(),
3070 NonNull::new(
3071 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3072 )
3073 .unwrap(),
3074 )?;
3075 }
3076 dst.copy_from_slice(&src[..dst.len()]);
3077 let task = store.0.current_guest_thread()?.task;
3078 let state = store.0.concurrent_state_mut()?;
3079 Waitable::Guest(task).set_event(
3080 state,
3081 Some(Event::Subtask {
3082 status: Status::Started,
3083 }),
3084 )?;
3085 Ok(())
3086 }),
3087 LiftResult {
3088 lift: Box::new(move |store, src| {
3089 let mut store = token.as_context_mut(store);
3092 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3094 my_src.push(ValRaw::u32(*results));
3095 }
3096
3097 unsafe {
3104 crate::Func::call_unchecked_raw(
3105 &mut store,
3106 return_.as_non_null(),
3107 my_src.as_mut_slice().into(),
3108 )?;
3109 }
3110
3111 let thread = store.0.current_guest_thread()?;
3112 let state = store.0.concurrent_state_mut()?;
3113 if sync_caller {
3114 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3115 if let ResultInfo::Stack { result_count } = &result_info {
3116 match result_count {
3117 0 => None,
3118 1 => Some(my_src[0]),
3119 _ => unreachable!(),
3120 }
3121 } else {
3122 None
3123 },
3124 );
3125 }
3126 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3127 }),
3128 ty: task_return_type,
3129 memory: NonNull::new(memory).map(SendSyncPtr::new),
3130 string_encoding,
3131 },
3132 Caller::Guest { thread: old_thread },
3133 None,
3134 self.runtime_instance(callee_instance),
3135 callee_async_typed,
3136 false,
3139 )?;
3140
3141 store.0.set_thread(guest_thread)?;
3144 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3145
3146 Ok(())
3147 }
3148
3149 unsafe fn call_callback<T>(
3154 self,
3155 mut store: StoreContextMut<T>,
3156 function: SendSyncPtr<VMFuncRef>,
3157 event: Event,
3158 handle: u32,
3159 ) -> Result<u32> {
3160 let (ordinal, result) = event.parts();
3161 let params = &mut [
3162 ValRaw::u32(ordinal),
3163 ValRaw::u32(handle),
3164 ValRaw::u32(result),
3165 ];
3166 unsafe {
3171 crate::Func::call_unchecked_raw(
3172 &mut store,
3173 function.as_non_null(),
3174 params.as_mut_slice().into(),
3175 )?;
3176 }
3177 Ok(params[0].get_u32())
3178 }
3179
3180 unsafe fn start_call<T: 'static>(
3193 self,
3194 mut store: StoreContextMut<T>,
3195 callback: *mut VMFuncRef,
3196 post_return: *mut VMFuncRef,
3197 callee: NonNull<VMFuncRef>,
3198 param_count: u32,
3199 result_count: u32,
3200 flags: u32,
3201 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3202 ) -> Result<u32> {
3203 let token = StoreToken::new(store.as_context_mut());
3204 let async_caller = storage.is_none();
3205 let guest_thread = store.0.current_guest_thread()?;
3206 let state = store.0.concurrent_state_mut()?;
3207
3208 if !state.event_loop_running {
3209 bail_bug!("Instance::start_call called without a running event loop");
3210 }
3211
3212 let callee = SendSyncPtr::new(callee);
3213 let param_count = usize::try_from(param_count)?;
3214 assert!(param_count <= MAX_FLAT_PARAMS);
3215 let result_count = usize::try_from(result_count)?;
3216 assert!(result_count <= MAX_FLAT_RESULTS);
3217
3218 let task = state.get_mut(guest_thread.task)?;
3219 let callee_async_typed = task.async_typed;
3220 let callee_instance = task.instance;
3221
3222 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3223
3224 if let Some(callback) = NonNull::new(callback) {
3225 let callback = SendSyncPtr::new(callback);
3229 task.callback = Some(Box::new(move |store, event, handle| {
3230 let store = token.as_context_mut(store);
3231 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3232 }));
3233 }
3234
3235 let Caller::Guest { thread: caller } = &task.caller else {
3236 bail_bug!("start_call unexpectedly invoked for host->guest call");
3239 };
3240 let caller = *caller;
3241 let caller_instance = state.get_mut(caller.task)?.instance;
3242
3243 unsafe {
3245 self.stage_call(
3246 store.as_context_mut(),
3247 guest_thread,
3248 callee,
3249 param_count,
3250 result_count,
3251 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3252 NonNull::new(callback).map(SendSyncPtr::new),
3253 NonNull::new(post_return).map(SendSyncPtr::new),
3254 false,
3255 )?;
3256 }
3257
3258 let old_do_not_suspend = if callee_async_typed {
3259 let state = store.0.instance_state(callee_instance).concurrent_state();
3266 let old_do_not_suspend = state.do_not_suspend;
3267 state.do_not_suspend = false;
3268 Some(old_do_not_suspend)
3269 } else {
3270 None
3271 };
3272
3273 let state = store.0.concurrent_state_mut()?;
3274
3275 let guest_waitable = Waitable::Guest(guest_thread.task);
3278 let old_set = guest_waitable.common(state)?.set;
3279 let set = state.get_mut(caller.thread)?.sync_call_set;
3280 guest_waitable.join(state, Some(set))?;
3281
3282 store.0.set_thread(CurrentThread::None)?;
3283
3284 let (status, waitable) = loop {
3300 store.0.suspend(SuspendReason::WaitingForGuestSubtask {
3301 caller,
3302 callee: guest_thread.task,
3303 })?;
3304
3305 if let Some(old_do_not_suspend) = old_do_not_suspend {
3306 store
3307 .0
3308 .instance_state(callee_instance)
3309 .concurrent_state()
3310 .do_not_suspend = old_do_not_suspend;
3311 }
3312
3313 let state = store.0.concurrent_state_mut()?;
3314
3315 log::trace!("taking event for {:?}", guest_thread.task);
3316 let event = guest_waitable.take_event(state)?;
3317 let Some(Event::Subtask { status }) = event else {
3318 bail_bug!("subtasks should only get subtask events, got {event:?}")
3319 };
3320
3321 log::trace!("status {status:?} for {:?}", guest_thread.task);
3322
3323 if status == Status::Returned {
3324 break (status, None);
3326 } else if async_caller {
3327 let handle = store
3331 .0
3332 .instance_state(caller_instance)
3333 .handle_table()
3334 .subtask_insert_guest(guest_thread.task.rep())?;
3335 store
3336 .0
3337 .concurrent_state_mut()?
3338 .get_mut(guest_thread.task)?
3339 .common
3340 .handle = Some(handle);
3341 break (status, Some(handle));
3342 } else {
3343 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3347 }
3348 };
3349
3350 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3351
3352 store.0.set_thread(caller)?;
3354 store
3355 .0
3356 .concurrent_state_mut()?
3357 .get_mut(caller.thread)?
3358 .state = GuestThreadState::Running;
3359 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3360
3361 if let Some(storage) = storage {
3362 let state = store.0.concurrent_state_mut()?;
3366 let task = state.get_mut(guest_thread.task)?;
3367 if let Some(result) = task.sync_result.take()? {
3368 if let Some(result) = result {
3369 storage[0] = MaybeUninit::new(result);
3370 }
3371
3372 if task.exited && task.ready_to_delete() {
3373 Waitable::Guest(guest_thread.task).delete_from(state)?;
3374 }
3375 }
3376 }
3377
3378 Ok(status.pack(waitable))
3379 }
3380
3381 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3394 self,
3395 mut store: StoreContextMut<'_, T>,
3396 host_task: EnteredHostTask,
3397 future: impl Future<Output = Result<R>> + Send + 'static,
3398 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool) -> Result<()> + Send + 'static,
3399 ) -> Result<u32> {
3400 let token = StoreToken::new(store.as_context_mut());
3401 let task = store.0.current_host_thread()?;
3402 let state = store.0.concurrent_state_mut()?;
3403
3404 let (join_handle, future) = JoinHandle::run(future);
3407 {
3408 let state = &mut state.get_mut(task)?.state;
3409 assert!(matches!(state, HostTaskState::CalleeStarted));
3410 *state = HostTaskState::CalleeRunning(join_handle);
3411 }
3412
3413 let mut future = Box::pin(future);
3414
3415 let poll = tls::set(store.0, || {
3420 future
3421 .as_mut()
3422 .poll(&mut Context::from_waker(&Waker::noop()))
3423 });
3424
3425 match poll {
3426 Poll::Ready(result) => {
3428 let result = result.transpose()?;
3429 lower(store.as_context_mut(), result, true)?;
3430 return Ok(Status::Returned.pack(None));
3431 }
3432
3433 Poll::Pending => {}
3435 }
3436
3437 let future = Box::pin(async move {
3445 let result = match future.await {
3446 Some(result) => Some(result?),
3447 None => None,
3448 };
3449 let on_complete = move |store: &mut dyn VMStore| {
3450 let mut store = token.as_context_mut(store);
3454 let old = store.0.set_thread(task)?;
3455
3456 let status = if result.is_some() {
3457 Status::Returned
3458 } else {
3459 Status::ReturnCancelled
3460 };
3461
3462 lower(store.as_context_mut(), result, false)?;
3463 let state = store.0.concurrent_state_mut()?;
3464 match &mut state.get_mut(task)?.state {
3465 HostTaskState::CalleeDone { .. } => {}
3468
3469 other => *other = HostTaskState::CalleeDone { cancelled: false },
3471 }
3472 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3473
3474 store.0.set_thread(old)?;
3475 Ok(())
3476 };
3477
3478 tls::get(move |store| {
3483 store
3484 .concurrent_state_mut()?
3485 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3486 on_complete,
3487 ))));
3488 Ok(())
3489 })
3490 });
3491
3492 let caller = match host_task {
3495 Some(pair) => pair.1,
3496 None => bail_bug!("host task wasn't created but should have been"),
3497 };
3498 let state = store.0.concurrent_state_mut()?;
3499 state.push_future(future);
3500 let instance = state.get_mut(caller.task)?.instance;
3501 let handle = store
3502 .0
3503 .instance_state(instance)
3504 .handle_table()
3505 .subtask_insert_host(task.rep())?;
3506 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3507 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3508
3509 store.0.set_thread(caller)?;
3513 Ok(Status::Started.pack(Some(handle)))
3514 }
3515
3516 pub(crate) fn task_return(
3519 self,
3520 store: &mut dyn VMStore,
3521 ty: TypeTupleIndex,
3522 options: OptionsIndex,
3523 storage: &[ValRaw],
3524 ) -> Result<()> {
3525 let guest_thread = store.current_guest_thread()?;
3526 let state = store.concurrent_state_mut()?;
3527 let lift = state
3528 .get_mut(guest_thread.task)?
3529 .lift_result
3530 .take()
3531 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3532 if !state.get_mut(guest_thread.task)?.result.is_none() {
3533 bail_bug!("task result unexpectedly already set");
3534 }
3535
3536 let CanonicalOptions {
3537 string_encoding,
3538 data_model,
3539 ..
3540 } = &self.id().get(store).component().env_component().options[options];
3541
3542 let invalid = ty != lift.ty
3543 || string_encoding != &lift.string_encoding
3544 || match data_model {
3545 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3546 Some(memory) => {
3547 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3548 let actual = self.id().get(store).runtime_memory(memory);
3549 expected != actual.as_ptr()
3550 }
3551 None => false,
3554 },
3555 CanonicalOptionsDataModel::Gc { .. } => true,
3557 };
3558
3559 if invalid {
3560 bail!(Trap::TaskReturnInvalid);
3561 }
3562
3563 log::trace!("task.return for {guest_thread:?}");
3564
3565 let result = (lift.lift)(store, storage)?;
3566 self.task_complete(store, guest_thread.task, result, Status::Returned)
3567 }
3568
3569 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3571 let guest_thread = store.current_guest_thread()?;
3572 let state = store.concurrent_state_mut()?;
3573 let task = state.get_mut(guest_thread.task)?;
3574 if !task.cancel_sent {
3575 bail!(Trap::TaskCancelNotCancelled);
3576 }
3577 _ = task
3578 .lift_result
3579 .take()
3580 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3581
3582 if !task.result.is_none() {
3583 bail_bug!("task result should not bet set yet");
3584 }
3585
3586 log::trace!("task.cancel for {guest_thread:?}");
3587
3588 self.task_complete(
3589 store,
3590 guest_thread.task,
3591 Box::new(DummyResult),
3592 Status::ReturnCancelled,
3593 )
3594 }
3595
3596 fn task_complete(
3602 self,
3603 store: &mut StoreOpaque,
3604 guest_task: TableId<GuestTask>,
3605 result: Box<dyn Any + Send + Sync>,
3606 status: Status,
3607 ) -> Result<()> {
3608 store
3609 .component_resource_tables(Some(self))?
3610 .validate_scope_exit()?;
3611
3612 let state = store.concurrent_state_mut()?;
3613 let task = state.get_mut(guest_task)?;
3614
3615 if let Caller::Host { tx, .. } = &mut task.caller {
3616 if let Some(tx) = tx.take() {
3617 _ = tx.send(result);
3618 }
3619 } else {
3620 task.result = Some(result);
3621 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3622 }
3623
3624 Ok(())
3625 }
3626
3627 pub(crate) fn waitable_set_new(
3629 self,
3630 store: &mut StoreOpaque,
3631 caller_instance: RuntimeComponentInstanceIndex,
3632 ) -> Result<u32> {
3633 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3634 let handle = store
3635 .instance_state(self.runtime_instance(caller_instance))
3636 .handle_table()
3637 .waitable_set_insert(set.rep())?;
3638 log::trace!("new waitable set {set:?} (handle {handle})");
3639 Ok(handle)
3640 }
3641
3642 pub(crate) fn waitable_set_drop(
3644 self,
3645 store: &mut StoreOpaque,
3646 caller_instance: RuntimeComponentInstanceIndex,
3647 set: u32,
3648 ) -> Result<()> {
3649 let rep = store
3650 .instance_state(self.runtime_instance(caller_instance))
3651 .handle_table()
3652 .waitable_set_remove(set)?;
3653
3654 log::trace!("drop waitable set {rep} (handle {set})");
3655
3656 if !store
3660 .concurrent_state_mut()?
3661 .get_mut(TableId::<WaitableSet>::new(rep))?
3662 .waiting
3663 .is_empty()
3664 {
3665 bail!(Trap::WaitableSetDropHasWaiters);
3666 }
3667
3668 store
3669 .concurrent_state_mut()?
3670 .delete(TableId::<WaitableSet>::new(rep))?;
3671
3672 Ok(())
3673 }
3674
3675 pub(crate) fn waitable_join(
3677 self,
3678 store: &mut StoreOpaque,
3679 caller_instance: RuntimeComponentInstanceIndex,
3680 waitable_handle: u32,
3681 set_handle: u32,
3682 ) -> Result<()> {
3683 let mut instance = self.id().get_mut(store);
3684 let waitable =
3685 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3686
3687 let set = if set_handle == 0 {
3688 None
3689 } else {
3690 let set = instance.instance_states().0[caller_instance]
3691 .handle_table()
3692 .waitable_set_rep(set_handle)?;
3693
3694 let state = store.concurrent_state_mut()?;
3695 if let Some(old) = waitable.common(state)?.set
3696 && state.get_mut(old)?.is_sync_call_set
3697 {
3698 bail!(Trap::WaitableSyncAndAsync);
3699 }
3700
3701 Some(TableId::<WaitableSet>::new(set))
3702 };
3703
3704 log::trace!(
3705 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3706 );
3707
3708 waitable.join(store.concurrent_state_mut()?, set)
3709 }
3710
3711 pub(crate) fn subtask_drop(
3713 self,
3714 store: &mut StoreOpaque,
3715 caller_instance: RuntimeComponentInstanceIndex,
3716 task_id: u32,
3717 ) -> Result<()> {
3718 self.waitable_join(store, caller_instance, task_id, 0)?;
3719
3720 let (rep, is_host) = store
3721 .instance_state(self.runtime_instance(caller_instance))
3722 .handle_table()
3723 .subtask_remove(task_id)?;
3724
3725 let concurrent_state = store.concurrent_state_mut()?;
3726 let (waitable, delete) = if is_host {
3727 let id = TableId::<HostTask>::new(rep);
3728 let task = concurrent_state.get_mut(id)?;
3729 match &task.state {
3730 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3731 HostTaskState::CalleeDone { .. } => {}
3732 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3733 bail_bug!("invalid state for callee in `subtask.drop`")
3734 }
3735 }
3736 (Waitable::Host(id), true)
3737 } else {
3738 let id = TableId::<GuestTask>::new(rep);
3739 let task = concurrent_state.get_mut(id)?;
3740 if task.lift_result.is_some() {
3741 bail!(Trap::SubtaskDropNotResolved);
3742 }
3743 (
3744 Waitable::Guest(id),
3745 concurrent_state.get_mut(id)?.ready_to_delete(),
3746 )
3747 };
3748
3749 waitable.common(concurrent_state)?.handle = None;
3750
3751 if waitable.take_event(concurrent_state)?.is_some() {
3754 bail!(Trap::SubtaskDropNotResolved);
3755 }
3756
3757 if delete {
3758 waitable.delete_from(concurrent_state)?;
3759 }
3760
3761 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3762 Ok(())
3763 }
3764
3765 pub(crate) fn waitable_set_wait(
3767 self,
3768 store: &mut StoreOpaque,
3769 options: OptionsIndex,
3770 set: u32,
3771 payload: u32,
3772 ) -> Result<u32> {
3773 let &CanonicalOptions {
3774 cancellable,
3775 instance: caller_instance,
3776 ..
3777 } = &self.id().get(store).component().env_component().options[options];
3778 let caller = self.runtime_instance(caller_instance);
3779 let rep = store
3780 .instance_state(self.runtime_instance(caller_instance))
3781 .handle_table()
3782 .waitable_set_rep(set)?;
3783
3784 self.waitable_check(
3785 store,
3786 caller,
3787 cancellable,
3788 WaitableCheck::Wait,
3789 WaitableCheckParams {
3790 set: TableId::new(rep),
3791 options,
3792 payload,
3793 },
3794 )
3795 }
3796
3797 pub(crate) fn waitable_set_poll(
3799 self,
3800 store: &mut StoreOpaque,
3801 options: OptionsIndex,
3802 set: u32,
3803 payload: u32,
3804 ) -> Result<u32> {
3805 let &CanonicalOptions {
3806 cancellable,
3807 instance: caller_instance,
3808 ..
3809 } = &self.id().get(store).component().env_component().options[options];
3810 let caller = self.runtime_instance(caller_instance);
3811 let rep = store
3812 .instance_state(caller)
3813 .handle_table()
3814 .waitable_set_rep(set)?;
3815
3816 self.waitable_check(
3817 store,
3818 caller,
3819 cancellable,
3820 WaitableCheck::Poll,
3821 WaitableCheckParams {
3822 set: TableId::new(rep),
3823 options,
3824 payload,
3825 },
3826 )
3827 }
3828
3829 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3831 let thread_id = store.current_guest_thread()?.thread;
3832 match store
3833 .concurrent_state_mut()?
3834 .get_mut(thread_id)?
3835 .instance_rep
3836 {
3837 Some(r) => Ok(r),
3838 None => bail_bug!("thread should have instance_rep by now"),
3839 }
3840 }
3841
3842 pub(crate) fn thread_new_indirect<T: 'static>(
3844 self,
3845 mut store: StoreContextMut<T>,
3846 runtime_instance: RuntimeComponentInstanceIndex,
3847 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3849 start_func_idx: u32,
3850 context: i32,
3851 ) -> Result<u32> {
3852 log::trace!("creating new thread");
3853
3854 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3855 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3856 let callee = instance
3857 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3858 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3859 if callee.type_index(store.0) != start_func_ty.type_index() {
3860 bail!(Trap::ThreadNewIndirectInvalidType);
3861 }
3862
3863 let token = StoreToken::new(store.as_context_mut());
3864 let start_func = Box::new(
3865 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3866 let old_thread = store.set_thread(guest_thread)?;
3867 log::trace!(
3868 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3869 );
3870
3871 let mut store = token.as_context_mut(store);
3872 let mut params = [ValRaw::i32(context)];
3873 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3876
3877 store.0.set_thread(old_thread)?;
3878
3879 let runtime_instance = self.runtime_instance(runtime_instance);
3880
3881 store
3884 .0
3885 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
3886
3887 store
3888 .0
3889 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
3890
3891 log::trace!("explicit thread {guest_thread:?} completed");
3892 let state = store.0.concurrent_state_mut()?;
3893 if let Some(t) = old_thread.guest() {
3894 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3895 }
3896 log::trace!("thread start: restored {old_thread:?} as current thread");
3897
3898 Ok(())
3899 },
3900 );
3901
3902 let current_thread = store.0.current_guest_thread()?;
3903 let state = store.0.concurrent_state_mut()?;
3904 let parent_task = current_thread.task;
3905
3906 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3907 let thread_id = state.push(new_thread)?;
3908 state.get_mut(parent_task)?.threads.insert(thread_id);
3909
3910 log::trace!("new thread with id {thread_id:?} created");
3911
3912 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3913 }
3914
3915 pub(crate) fn resume_thread(
3916 self,
3917 store: &mut StoreOpaque,
3918 runtime_instance: RuntimeComponentInstanceIndex,
3919 thread_idx: u32,
3920 how: ResumeThread,
3921 ) -> Result<bool> {
3922 let thread_id =
3923 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3924 let state = store.concurrent_state_mut()?;
3925 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3926
3927 if store.current_guest_thread()? == guest_thread {
3928 bail!(Trap::CannotResumeThread);
3929 }
3930
3931 let state = store.concurrent_state_mut()?;
3932 let thread = state.get_mut(guest_thread.thread)?;
3933 let priority = match how {
3934 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
3935 ResumeThread::ResumeLater => Priority::Low,
3936 };
3937
3938 match (&how, &thread.state) {
3939 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
3941 (ResumeThread::Promote, _) => return Ok(false),
3942
3943 (
3946 ResumeThread::Resume | ResumeThread::ResumeLater,
3947 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
3948 ) => {}
3949 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
3950 bail!(Trap::CannotResumeThread)
3951 }
3952 }
3953
3954 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3955 GuestThreadState::NotStartedExplicit(start_func) => {
3956 log::trace!("starting thread {guest_thread:?}");
3957 let guest_call = WorkItem::GuestCall {
3958 instance: self.runtime_instance(runtime_instance),
3959 call: GuestCall {
3960 thread: guest_thread,
3961 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3962 start_func(store, guest_thread)
3963 })),
3964 },
3965 };
3966 store
3967 .concurrent_state_mut()?
3968 .push_work_item(guest_call, priority)?;
3969 }
3970 GuestThreadState::Suspended(fiber) => {
3971 log::trace!("resuming thread {thread_id:?} that was suspended");
3972 store.concurrent_state_mut()?.push_work_item(
3973 WorkItem::ResumeFiber {
3974 instance: self.runtime_instance(runtime_instance),
3975 thread: guest_thread,
3976 fiber,
3977 },
3978 priority,
3979 )?;
3980 }
3981 GuestThreadState::Ready { fiber, cancellable } => {
3982 log::trace!("resuming thread {thread_id:?} that was ready");
3983 thread.state = GuestThreadState::Ready { fiber, cancellable };
3984 store
3985 .concurrent_state_mut()?
3986 .promote_thread_work_item(guest_thread)?;
3987 }
3988 other @ (GuestThreadState::NotStartedImplicit
3989 | GuestThreadState::Running
3990 | GuestThreadState::Completed) => {
3991 thread.state = other;
3992 }
3993 }
3994 Ok(true)
3995 }
3996
3997 fn add_guest_thread_to_instance_table(
3998 self,
3999 thread_id: TableId<GuestThread>,
4000 store: &mut StoreOpaque,
4001 runtime_instance: RuntimeComponentInstanceIndex,
4002 ) -> Result<u32> {
4003 let guest_id = store
4004 .instance_state(self.runtime_instance(runtime_instance))
4005 .thread_handle_table()
4006 .guest_thread_insert(thread_id.rep())?;
4007 store
4008 .concurrent_state_mut()?
4009 .get_mut(thread_id)?
4010 .instance_rep = Some(guest_id);
4011 Ok(guest_id)
4012 }
4013
4014 pub(crate) fn suspension_intrinsic(
4018 self,
4019 store: &mut StoreOpaque,
4020 caller: RuntimeComponentInstanceIndex,
4021 cancellable: bool,
4022 yielding: bool,
4023 to_thread: SuspensionTarget,
4024 ) -> Result<WaitResult> {
4025 if cancellable && store.take_pending_cancellation()? {
4027 return Ok(WaitResult::Cancelled);
4028 }
4029
4030 let check_suspend = match to_thread {
4031 SuspensionTarget::Promote(thread) => {
4032 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4033 }
4034 SuspensionTarget::Resume(thread) => {
4035 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4036 bail_bug!(
4037 "`resume_thread` should only ever return false \
4038 when `ResumeThread::Promote` is passed to it"
4039 );
4040 }
4041 false
4042 }
4043 SuspensionTarget::None => true,
4044 };
4045
4046 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4047 return if yielding {
4048 Ok(WaitResult::Completed)
4049 } else {
4050 Err(Trap::CannotBlockSyncTask.into())
4051 };
4052 }
4053
4054 let guest_thread = store.current_guest_thread()?;
4055
4056 let reason = if yielding {
4057 SuspendReason::Yielding {
4058 thread: guest_thread,
4059 cancellable,
4060 }
4061 } else {
4062 SuspendReason::ExplicitlySuspending {
4063 thread: guest_thread,
4064 }
4065 };
4066
4067 store.suspend(reason)?;
4068
4069 if cancellable && store.take_pending_cancellation()? {
4070 Ok(WaitResult::Cancelled)
4071 } else {
4072 Ok(WaitResult::Completed)
4073 }
4074 }
4075
4076 fn waitable_check(
4078 self,
4079 store: &mut StoreOpaque,
4080 caller: RuntimeInstance,
4081 cancellable: bool,
4082 check: WaitableCheck,
4083 params: WaitableCheckParams,
4084 ) -> Result<u32> {
4085 let guest_thread = store.current_guest_thread()?;
4086
4087 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4088
4089 let state = store.concurrent_state_mut()?;
4090 let task = state.get_mut(guest_thread.task)?;
4091
4092 match &check {
4095 WaitableCheck::Wait => {
4096 let set = params.set;
4097
4098 if (task.event.is_none()
4099 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
4100 && state.get_mut(set)?.ready.is_empty()
4101 {
4102 store.switch_or_trap_if_may_not_suspend(caller)?;
4103
4104 if cancellable {
4105 let old = store
4106 .concurrent_state_mut()?
4107 .get_mut(guest_thread.thread)?
4108 .wake_on_cancel
4109 .replace(set);
4110 if !old.is_none() {
4111 bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
4112 }
4113 }
4114
4115 store.suspend(SuspendReason::Waiting {
4116 set,
4117 thread: guest_thread,
4118 })?;
4119 }
4120 }
4121 WaitableCheck::Poll => {}
4122 }
4123
4124 log::trace!(
4125 "waitable check for {guest_thread:?}; set {:?}, part two",
4126 params.set
4127 );
4128
4129 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
4131
4132 let (ordinal, handle, result) = match &check {
4133 WaitableCheck::Wait => {
4134 let (event, waitable) = match event {
4135 Some(p) => p,
4136 None => bail_bug!("event expected to be present"),
4137 };
4138 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4139 let (ordinal, result) = event.parts();
4140 (ordinal, handle, result)
4141 }
4142 WaitableCheck::Poll => {
4143 if let Some((event, waitable)) = event {
4144 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4145 let (ordinal, result) = event.parts();
4146 (ordinal, handle, result)
4147 } else {
4148 log::trace!(
4149 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4150 guest_thread.task,
4151 params.set
4152 );
4153 let (ordinal, result) = Event::None.parts();
4154 (ordinal, 0, result)
4155 }
4156 }
4157 };
4158 let memory = self.options_memory_mut(store, params.options);
4159 let ptr = crate::component::func::validate_inbounds_dynamic(
4160 &CanonicalAbiInfo::POINTER_PAIR,
4161 memory,
4162 &ValRaw::u32(params.payload),
4163 )?;
4164 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4165 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4166 Ok(ordinal)
4167 }
4168
4169 pub(crate) fn subtask_cancel(
4171 self,
4172 store: &mut StoreOpaque,
4173 caller_instance: RuntimeComponentInstanceIndex,
4174 async_: bool,
4175 task_id: u32,
4176 ) -> Result<u32> {
4177 let (rep, is_host) = store
4178 .instance_state(self.runtime_instance(caller_instance))
4179 .handle_table()
4180 .subtask_rep(task_id)?;
4181 let waitable = if is_host {
4182 Waitable::Host(TableId::<HostTask>::new(rep))
4183 } else {
4184 Waitable::Guest(TableId::<GuestTask>::new(rep))
4185 };
4186 let concurrent_state = store.concurrent_state_mut()?;
4187
4188 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4189
4190 waitable.trap_if_in_waitable_set(concurrent_state)?;
4191
4192 let needs_block;
4193 if let Waitable::Host(host_task) = waitable {
4194 let state = &mut concurrent_state.get_mut(host_task)?.state;
4195 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
4196 HostTaskState::CalleeRunning(handle) => {
4203 handle.abort();
4204 needs_block = true;
4205 }
4206
4207 HostTaskState::CalleeDone { cancelled } => {
4210 if cancelled {
4211 bail!(Trap::SubtaskCancelAfterTerminal);
4212 } else {
4213 needs_block = false;
4216 }
4217 }
4218
4219 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4222 bail_bug!("invalid states for host callee")
4223 }
4224 }
4225 } else {
4226 let guest_task = TableId::<GuestTask>::new(rep);
4227 let task = concurrent_state.get_mut(guest_task)?;
4228 if !task.already_lowered_parameters() {
4229 store.cancel_guest_subtask_without_lowered_parameters(
4230 self.runtime_instance(caller_instance),
4231 guest_task,
4232 )?;
4233 return Ok(Status::StartCancelled as u32);
4234 } else if !task.returned_or_cancelled() {
4235 task.cancel_sent = true;
4238 task.event = Some(Event::Cancelled);
4243 let runtime_instance = task.instance;
4244 for thread in task.threads.clone() {
4245 let thread = QualifiedThreadId {
4246 task: guest_task,
4247 thread,
4248 };
4249 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4250
4251 let yield_ = |store: &mut StoreOpaque| {
4252 let state = store.instance_state(runtime_instance).concurrent_state();
4257 let old_do_not_suspend = state.do_not_suspend;
4258 state.do_not_suspend = false;
4259
4260 let caller = store.current_guest_thread()?;
4261
4262 let state = store.concurrent_state_mut()?;
4267 let set = state.get_mut(caller.thread)?.sync_call_set;
4268 waitable.join(state, Some(set))?;
4269
4270 store.suspend(SuspendReason::Yielding {
4271 thread: caller,
4272 cancellable: false,
4273 })?;
4274
4275 let state = store.concurrent_state_mut()?;
4276 waitable.join(state, None)?;
4277
4278 store
4279 .instance_state(runtime_instance)
4280 .concurrent_state()
4281 .do_not_suspend = old_do_not_suspend;
4282
4283 Ok::<(), crate::Error>(())
4284 };
4285
4286 if let Some(set) = thread_mut.wake_on_cancel.take() {
4287 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4289 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber {
4290 instance: runtime_instance,
4291 thread,
4292 fiber,
4293 },
4294 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4295 instance: runtime_instance,
4296 call: GuestCall {
4297 thread,
4298 kind: GuestCallKind::DeliverEvent {
4299 instance,
4300 set: None,
4301 },
4302 },
4303 },
4304 Some(WaitMode::Caller { .. }) => {
4305 bail_bug!("unexpected `WaitMode::Caller` in wake_on_cancel set")
4306 }
4307 None => bail_bug!("thread not present in wake_on_cancel set"),
4308 };
4309 concurrent_state.set_switch_item(item)?;
4310
4311 yield_(store)?;
4312
4313 break;
4314 } else if let GuestThreadState::Ready {
4315 cancellable: true, ..
4316 } = &thread_mut.state
4317 {
4318 if !concurrent_state.promote_thread_work_item(thread)? {
4321 bail_bug!("a ready thread should have been promotable");
4322 }
4323
4324 yield_(store)?;
4325
4326 break;
4327 }
4328 }
4329
4330 needs_block = !store
4333 .concurrent_state_mut()?
4334 .get_mut(guest_task)?
4335 .returned_or_cancelled()
4336 } else {
4337 needs_block = false;
4338 }
4339 };
4340
4341 if needs_block {
4345 if async_ {
4346 return Ok(BLOCKED);
4347 }
4348
4349 store.wait_for_event(
4352 self.runtime_instance(caller_instance),
4353 waitable,
4354 if is_host {
4355 WaitReason::Other
4356 } else {
4357 WaitReason::GuestSubtask(TableId::<GuestTask>::new(rep))
4358 },
4359 )?;
4360
4361 }
4363
4364 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4365 if let Some(Event::Subtask {
4366 status: status @ (Status::Returned | Status::ReturnCancelled),
4367 }) = event
4368 {
4369 Ok(status as u32)
4370 } else {
4371 bail!(Trap::SubtaskCancelAfterTerminal);
4372 }
4373 }
4374}
4375
4376pub trait VMComponentAsyncStore {
4384 unsafe fn prepare_call(
4390 &mut self,
4391 instance: Instance,
4392 memory: *mut VMMemoryDefinition,
4393 start: NonNull<VMFuncRef>,
4394 return_: NonNull<VMFuncRef>,
4395 caller_instance: RuntimeComponentInstanceIndex,
4396 callee_instance: RuntimeComponentInstanceIndex,
4397 task_return_type: TypeTupleIndex,
4398 callee_async: bool,
4399 string_encoding: StringEncoding,
4400 result_count: u32,
4401 storage: *mut ValRaw,
4402 storage_len: usize,
4403 ) -> Result<()>;
4404
4405 unsafe fn sync_start(
4408 &mut self,
4409 instance: Instance,
4410 callback: *mut VMFuncRef,
4411 callee: NonNull<VMFuncRef>,
4412 param_count: u32,
4413 storage: *mut MaybeUninit<ValRaw>,
4414 storage_len: usize,
4415 ) -> Result<()>;
4416
4417 unsafe fn async_start(
4420 &mut self,
4421 instance: Instance,
4422 callback: *mut VMFuncRef,
4423 post_return: *mut VMFuncRef,
4424 callee: NonNull<VMFuncRef>,
4425 param_count: u32,
4426 result_count: u32,
4427 flags: u32,
4428 ) -> Result<u32>;
4429
4430 fn future_write(
4432 &mut self,
4433 instance: Instance,
4434 caller: RuntimeComponentInstanceIndex,
4435 ty: TypeFutureTableIndex,
4436 options: OptionsIndex,
4437 future: u32,
4438 address: u32,
4439 ) -> Result<u32>;
4440
4441 fn future_read(
4443 &mut self,
4444 instance: Instance,
4445 caller: RuntimeComponentInstanceIndex,
4446 ty: TypeFutureTableIndex,
4447 options: OptionsIndex,
4448 future: u32,
4449 address: u32,
4450 ) -> Result<u32>;
4451
4452 fn future_drop_writable(
4454 &mut self,
4455 instance: Instance,
4456 ty: TypeFutureTableIndex,
4457 writer: u32,
4458 ) -> Result<()>;
4459
4460 fn stream_write(
4462 &mut self,
4463 instance: Instance,
4464 caller: RuntimeComponentInstanceIndex,
4465 ty: TypeStreamTableIndex,
4466 options: OptionsIndex,
4467 stream: u32,
4468 address: u32,
4469 count: u32,
4470 ) -> Result<u32>;
4471
4472 fn stream_read(
4474 &mut self,
4475 instance: Instance,
4476 caller: RuntimeComponentInstanceIndex,
4477 ty: TypeStreamTableIndex,
4478 options: OptionsIndex,
4479 stream: u32,
4480 address: u32,
4481 count: u32,
4482 ) -> Result<u32>;
4483
4484 fn flat_stream_write(
4487 &mut self,
4488 instance: Instance,
4489 caller: RuntimeComponentInstanceIndex,
4490 ty: TypeStreamTableIndex,
4491 options: OptionsIndex,
4492 payload_size: u32,
4493 payload_align: u32,
4494 stream: u32,
4495 address: u32,
4496 count: u32,
4497 ) -> Result<u32>;
4498
4499 fn flat_stream_read(
4502 &mut self,
4503 instance: Instance,
4504 caller: RuntimeComponentInstanceIndex,
4505 ty: TypeStreamTableIndex,
4506 options: OptionsIndex,
4507 payload_size: u32,
4508 payload_align: u32,
4509 stream: u32,
4510 address: u32,
4511 count: u32,
4512 ) -> Result<u32>;
4513
4514 fn stream_drop_writable(
4516 &mut self,
4517 instance: Instance,
4518 ty: TypeStreamTableIndex,
4519 writer: u32,
4520 ) -> Result<()>;
4521
4522 fn error_context_debug_message(
4524 &mut self,
4525 instance: Instance,
4526 ty: TypeComponentLocalErrorContextTableIndex,
4527 options: OptionsIndex,
4528 err_ctx_handle: u32,
4529 debug_msg_address: u32,
4530 ) -> Result<()>;
4531
4532 fn thread_new_indirect(
4534 &mut self,
4535 instance: Instance,
4536 caller: RuntimeComponentInstanceIndex,
4537 func_ty_idx: TypeFuncIndex,
4538 start_func_table_idx: RuntimeTableIndex,
4539 start_func_idx: u32,
4540 context: i32,
4541 ) -> Result<u32>;
4542}
4543
4544impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4546 unsafe fn prepare_call(
4547 &mut self,
4548 instance: Instance,
4549 memory: *mut VMMemoryDefinition,
4550 start: NonNull<VMFuncRef>,
4551 return_: NonNull<VMFuncRef>,
4552 caller_instance: RuntimeComponentInstanceIndex,
4553 callee_instance: RuntimeComponentInstanceIndex,
4554 task_return_type: TypeTupleIndex,
4555 callee_async: bool,
4556 string_encoding: StringEncoding,
4557 result_count_or_max_if_async: u32,
4558 storage: *mut ValRaw,
4559 storage_len: usize,
4560 ) -> Result<()> {
4561 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4565
4566 unsafe {
4567 instance.prepare_call(
4568 StoreContextMut(self),
4569 start,
4570 return_,
4571 caller_instance,
4572 callee_instance,
4573 task_return_type,
4574 callee_async,
4575 memory,
4576 string_encoding,
4577 match result_count_or_max_if_async {
4578 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4579 params,
4580 has_result: false,
4581 },
4582 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4583 params,
4584 has_result: true,
4585 },
4586 result_count => CallerInfo::Sync {
4587 params,
4588 result_count,
4589 },
4590 },
4591 )
4592 }
4593 }
4594
4595 unsafe fn sync_start(
4596 &mut self,
4597 instance: Instance,
4598 callback: *mut VMFuncRef,
4599 callee: NonNull<VMFuncRef>,
4600 param_count: u32,
4601 storage: *mut MaybeUninit<ValRaw>,
4602 storage_len: usize,
4603 ) -> Result<()> {
4604 unsafe {
4605 instance
4606 .start_call(
4607 StoreContextMut(self),
4608 callback,
4609 ptr::null_mut(),
4610 callee,
4611 param_count,
4612 1,
4613 START_FLAG_ASYNC_CALLEE,
4614 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4618 )
4619 .map(drop)
4620 }
4621 }
4622
4623 unsafe fn async_start(
4624 &mut self,
4625 instance: Instance,
4626 callback: *mut VMFuncRef,
4627 post_return: *mut VMFuncRef,
4628 callee: NonNull<VMFuncRef>,
4629 param_count: u32,
4630 result_count: u32,
4631 flags: u32,
4632 ) -> Result<u32> {
4633 unsafe {
4634 instance.start_call(
4635 StoreContextMut(self),
4636 callback,
4637 post_return,
4638 callee,
4639 param_count,
4640 result_count,
4641 flags,
4642 None,
4643 )
4644 }
4645 }
4646
4647 fn future_write(
4648 &mut self,
4649 instance: Instance,
4650 caller: RuntimeComponentInstanceIndex,
4651 ty: TypeFutureTableIndex,
4652 options: OptionsIndex,
4653 future: u32,
4654 address: u32,
4655 ) -> Result<u32> {
4656 instance
4657 .guest_write(
4658 StoreContextMut(self),
4659 caller,
4660 TransmitIndex::Future(ty),
4661 options,
4662 None,
4663 future,
4664 address,
4665 1,
4666 )
4667 .map(|result| result.encode())
4668 }
4669
4670 fn future_read(
4671 &mut self,
4672 instance: Instance,
4673 caller: RuntimeComponentInstanceIndex,
4674 ty: TypeFutureTableIndex,
4675 options: OptionsIndex,
4676 future: u32,
4677 address: u32,
4678 ) -> Result<u32> {
4679 instance
4680 .guest_read(
4681 StoreContextMut(self),
4682 caller,
4683 TransmitIndex::Future(ty),
4684 options,
4685 None,
4686 future,
4687 address,
4688 1,
4689 )
4690 .map(|result| result.encode())
4691 }
4692
4693 fn stream_write(
4694 &mut self,
4695 instance: Instance,
4696 caller: RuntimeComponentInstanceIndex,
4697 ty: TypeStreamTableIndex,
4698 options: OptionsIndex,
4699 stream: u32,
4700 address: u32,
4701 count: u32,
4702 ) -> Result<u32> {
4703 instance
4704 .guest_write(
4705 StoreContextMut(self),
4706 caller,
4707 TransmitIndex::Stream(ty),
4708 options,
4709 None,
4710 stream,
4711 address,
4712 count,
4713 )
4714 .map(|result| result.encode())
4715 }
4716
4717 fn stream_read(
4718 &mut self,
4719 instance: Instance,
4720 caller: RuntimeComponentInstanceIndex,
4721 ty: TypeStreamTableIndex,
4722 options: OptionsIndex,
4723 stream: u32,
4724 address: u32,
4725 count: u32,
4726 ) -> Result<u32> {
4727 instance
4728 .guest_read(
4729 StoreContextMut(self),
4730 caller,
4731 TransmitIndex::Stream(ty),
4732 options,
4733 None,
4734 stream,
4735 address,
4736 count,
4737 )
4738 .map(|result| result.encode())
4739 }
4740
4741 fn future_drop_writable(
4742 &mut self,
4743 instance: Instance,
4744 ty: TypeFutureTableIndex,
4745 writer: u32,
4746 ) -> Result<()> {
4747 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4748 }
4749
4750 fn flat_stream_write(
4751 &mut self,
4752 instance: Instance,
4753 caller: RuntimeComponentInstanceIndex,
4754 ty: TypeStreamTableIndex,
4755 options: OptionsIndex,
4756 payload_size: u32,
4757 payload_align: u32,
4758 stream: u32,
4759 address: u32,
4760 count: u32,
4761 ) -> Result<u32> {
4762 instance
4763 .guest_write(
4764 StoreContextMut(self),
4765 caller,
4766 TransmitIndex::Stream(ty),
4767 options,
4768 Some(FlatAbi {
4769 size: payload_size,
4770 align: payload_align,
4771 }),
4772 stream,
4773 address,
4774 count,
4775 )
4776 .map(|result| result.encode())
4777 }
4778
4779 fn flat_stream_read(
4780 &mut self,
4781 instance: Instance,
4782 caller: RuntimeComponentInstanceIndex,
4783 ty: TypeStreamTableIndex,
4784 options: OptionsIndex,
4785 payload_size: u32,
4786 payload_align: u32,
4787 stream: u32,
4788 address: u32,
4789 count: u32,
4790 ) -> Result<u32> {
4791 instance
4792 .guest_read(
4793 StoreContextMut(self),
4794 caller,
4795 TransmitIndex::Stream(ty),
4796 options,
4797 Some(FlatAbi {
4798 size: payload_size,
4799 align: payload_align,
4800 }),
4801 stream,
4802 address,
4803 count,
4804 )
4805 .map(|result| result.encode())
4806 }
4807
4808 fn stream_drop_writable(
4809 &mut self,
4810 instance: Instance,
4811 ty: TypeStreamTableIndex,
4812 writer: u32,
4813 ) -> Result<()> {
4814 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4815 }
4816
4817 fn error_context_debug_message(
4818 &mut self,
4819 instance: Instance,
4820 ty: TypeComponentLocalErrorContextTableIndex,
4821 options: OptionsIndex,
4822 err_ctx_handle: u32,
4823 debug_msg_address: u32,
4824 ) -> Result<()> {
4825 instance.error_context_debug_message(
4826 StoreContextMut(self),
4827 ty,
4828 options,
4829 err_ctx_handle,
4830 debug_msg_address,
4831 )
4832 }
4833
4834 fn thread_new_indirect(
4835 &mut self,
4836 instance: Instance,
4837 caller: RuntimeComponentInstanceIndex,
4838 func_ty_idx: TypeFuncIndex,
4839 start_func_table_idx: RuntimeTableIndex,
4840 start_func_idx: u32,
4841 context: i32,
4842 ) -> Result<u32> {
4843 instance.thread_new_indirect(
4844 StoreContextMut(self),
4845 caller,
4846 func_ty_idx,
4847 start_func_table_idx,
4848 start_func_idx,
4849 context,
4850 )
4851 }
4852}
4853
4854type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4855
4856pub(crate) struct HostTask {
4860 common: WaitableCommon,
4861
4862 caller: TableId<GuestTask>,
4869
4870 call_context: CallContext,
4873
4874 state: HostTaskState,
4875}
4876
4877enum HostTaskState {
4878 CalleeStarted,
4883
4884 CalleeRunning(JoinHandle),
4889
4890 CalleeFinished(LiftedResult),
4894
4895 CalleeDone { cancelled: bool },
4898}
4899
4900impl HostTask {
4901 fn new(caller: TableId<GuestTask>, state: HostTaskState) -> Self {
4902 Self {
4903 common: WaitableCommon::default(),
4904 call_context: CallContext::default(),
4905 caller,
4906 state,
4907 }
4908 }
4909}
4910
4911impl TableDebug for HostTask {
4912 fn type_name() -> &'static str {
4913 "HostTask"
4914 }
4915}
4916
4917type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4918
4919enum Caller {
4921 Host {
4923 tx: Option<oneshot::Sender<LiftedResult>>,
4925 host_future_present: bool,
4928 caller: CurrentThread,
4932 },
4933 Guest {
4935 thread: QualifiedThreadId,
4937 },
4938}
4939
4940struct LiftResult {
4943 lift: RawLift,
4944 ty: TypeTupleIndex,
4945 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4946 string_encoding: StringEncoding,
4947}
4948
4949#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4954pub(crate) struct QualifiedThreadId {
4955 task: TableId<GuestTask>,
4956 thread: TableId<GuestThread>,
4957}
4958
4959impl QualifiedThreadId {
4960 fn qualify(
4961 state: &mut ConcurrentState,
4962 thread: TableId<GuestThread>,
4963 ) -> Result<QualifiedThreadId> {
4964 Ok(QualifiedThreadId {
4965 task: state.get_mut(thread)?.parent_task,
4966 thread,
4967 })
4968 }
4969}
4970
4971impl fmt::Debug for QualifiedThreadId {
4972 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4973 f.debug_tuple("QualifiedThreadId")
4974 .field(&self.task.rep())
4975 .field(&self.thread.rep())
4976 .finish()
4977 }
4978}
4979
4980enum GuestThreadState {
4981 NotStartedImplicit,
4982 NotStartedExplicit(
4983 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4984 ),
4985 Running,
4986 Suspended(StoreFiber<'static>),
4987 Ready {
4988 fiber: StoreFiber<'static>,
4989 cancellable: bool,
4990 },
4991 Completed,
4992}
4993
4994impl fmt::Debug for GuestThreadState {
4995 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4996 match self {
4997 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
4998 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
4999 Self::Running => f.debug_tuple("Running").finish(),
5000 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
5001 Self::Ready { cancellable, .. } => f
5002 .debug_struct("Ready")
5003 .field("cancellable", cancellable)
5004 .finish(),
5005 Self::Completed => f.debug_tuple("Completed").finish(),
5006 }
5007 }
5008}
5009
5010pub struct GuestThread {
5011 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5014 parent_task: TableId<GuestTask>,
5016 wake_on_cancel: Option<TableId<WaitableSet>>,
5019 state: GuestThreadState,
5021 instance_rep: Option<u32>,
5024 sync_call_set: TableId<WaitableSet>,
5026 old_do_not_suspend: Option<bool>,
5029}
5030
5031impl GuestThread {
5032 fn from_instance(
5035 state: Pin<&mut ComponentInstance>,
5036 caller_instance: RuntimeComponentInstanceIndex,
5037 guest_thread: u32,
5038 ) -> Result<TableId<Self>> {
5039 let rep = state.instance_states().0[caller_instance]
5040 .thread_handle_table()
5041 .guest_thread_rep(guest_thread)?;
5042 Ok(TableId::new(rep))
5043 }
5044
5045 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5046 let sync_call_set = state.push(WaitableSet {
5047 is_sync_call_set: true,
5048 ..WaitableSet::default()
5049 })?;
5050 Ok(Self {
5051 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5052 parent_task,
5053 wake_on_cancel: None,
5054 state: GuestThreadState::NotStartedImplicit,
5055 instance_rep: None,
5056 sync_call_set,
5057 old_do_not_suspend: None,
5058 })
5059 }
5060
5061 fn new_explicit(
5062 state: &mut ConcurrentState,
5063 parent_task: TableId<GuestTask>,
5064 start_func: Box<
5065 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5066 >,
5067 ) -> Result<Self> {
5068 let sync_call_set = state.push(WaitableSet {
5069 is_sync_call_set: true,
5070 ..WaitableSet::default()
5071 })?;
5072 Ok(Self {
5073 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5074 parent_task,
5075 wake_on_cancel: None,
5076 state: GuestThreadState::NotStartedExplicit(start_func),
5077 instance_rep: None,
5078 sync_call_set,
5079 old_do_not_suspend: None,
5080 })
5081 }
5082}
5083
5084impl TableDebug for GuestThread {
5085 fn type_name() -> &'static str {
5086 "GuestThread"
5087 }
5088}
5089
5090enum SyncResult {
5091 NotProduced,
5092 Produced(Option<ValRaw>),
5093 Taken,
5094}
5095
5096impl SyncResult {
5097 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5098 Ok(match mem::replace(self, SyncResult::Taken) {
5099 SyncResult::NotProduced => None,
5100 SyncResult::Produced(val) => Some(val),
5101 SyncResult::Taken => {
5102 bail_bug!("attempted to take a synchronous result that was already taken")
5103 }
5104 })
5105 }
5106}
5107
5108#[derive(Debug)]
5109enum HostFutureState {
5110 NotApplicable,
5111 Live,
5112 Dropped,
5113}
5114
5115pub(crate) struct GuestTask {
5117 common: WaitableCommon,
5119 lower_params: Option<RawLower>,
5121 lift_result: Option<LiftResult>,
5123 result: Option<LiftedResult>,
5126 callback: Option<CallbackFn>,
5129 caller: Caller,
5131 call_context: CallContext,
5136 sync_result: SyncResult,
5139 cancel_sent: bool,
5142 starting_sent: bool,
5145 instance: RuntimeInstance,
5152 event: Option<Event>,
5155 exited: bool,
5157 threads: HashSet<TableId<GuestThread>>,
5159 host_future_state: HostFutureState,
5162 async_typed: bool,
5165 async_lifted: bool,
5168
5169 decremented_interesting_task_count: bool,
5170 switch_item: Option<WorkItem>,
5171}
5172
5173impl GuestTask {
5174 fn already_lowered_parameters(&self) -> bool {
5175 self.lower_params.is_none()
5177 }
5178
5179 fn returned_or_cancelled(&self) -> bool {
5180 self.lift_result.is_none()
5182 }
5183
5184 fn ready_to_delete(&self) -> bool {
5185 let threads_completed = self.threads.is_empty();
5186 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5187 let pending_completion_event = matches!(
5188 self.common.event,
5189 Some(Event::Subtask {
5190 status: Status::Returned | Status::ReturnCancelled
5191 })
5192 );
5193 let ready = threads_completed
5194 && !has_sync_result
5195 && !pending_completion_event
5196 && !matches!(self.host_future_state, HostFutureState::Live);
5197 log::trace!(
5198 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5199 threads_completed,
5200 has_sync_result,
5201 pending_completion_event,
5202 self.host_future_state
5203 );
5204 ready
5205 }
5206
5207 fn new(
5208 state: &mut ConcurrentState,
5209 lower_params: RawLower,
5210 lift_result: LiftResult,
5211 caller: Caller,
5212 callback: Option<CallbackFn>,
5213 instance: RuntimeInstance,
5214 async_typed: bool,
5215 async_lifted: bool,
5216 ) -> Result<QualifiedThreadId> {
5217 let host_future_state = match &caller {
5218 Caller::Guest { .. } => HostFutureState::NotApplicable,
5219 Caller::Host {
5220 host_future_present,
5221 ..
5222 } => {
5223 if *host_future_present {
5224 HostFutureState::Live
5225 } else {
5226 HostFutureState::NotApplicable
5227 }
5228 }
5229 };
5230 let task = state.push(Self {
5231 common: WaitableCommon::default(),
5232 lower_params: Some(lower_params),
5233 lift_result: Some(lift_result),
5234 result: None,
5235 callback,
5236 caller,
5237 call_context: CallContext::default(),
5238 sync_result: SyncResult::NotProduced,
5239 cancel_sent: false,
5240 starting_sent: false,
5241 instance,
5242 event: None,
5243 exited: false,
5244 threads: HashSet::new(),
5245 host_future_state,
5246 async_typed,
5247 async_lifted,
5248 decremented_interesting_task_count: false,
5249 switch_item: None,
5250 })?;
5251 let new_thread = GuestThread::new_implicit(state, task)?;
5252 let thread = state.push(new_thread)?;
5253 state.get_mut(task)?.threads.insert(thread);
5254 state.interesting_tasks += 1;
5255 let thread = QualifiedThreadId { task, thread };
5256 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5257 Ok(thread)
5258 }
5259}
5260
5261impl TableDebug for GuestTask {
5262 fn type_name() -> &'static str {
5263 "GuestTask"
5264 }
5265}
5266
5267#[derive(Default)]
5269struct WaitableCommon {
5270 event: Option<Event>,
5272 set: Option<TableId<WaitableSet>>,
5274 handle: Option<u32>,
5276}
5277
5278#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5280enum Waitable {
5281 Host(TableId<HostTask>),
5283 Guest(TableId<GuestTask>),
5285 Transmit(TableId<TransmitHandle>),
5287}
5288
5289impl Waitable {
5290 fn from_instance(
5293 state: Pin<&mut ComponentInstance>,
5294 caller_instance: RuntimeComponentInstanceIndex,
5295 waitable: u32,
5296 ) -> Result<Self> {
5297 use crate::runtime::vm::component::Waitable;
5298
5299 let (waitable, kind) = state.instance_states().0[caller_instance]
5300 .handle_table()
5301 .waitable_rep(waitable)?;
5302
5303 Ok(match kind {
5304 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5305 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5306 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5307 })
5308 }
5309
5310 fn rep(&self) -> u32 {
5312 match self {
5313 Self::Host(id) => id.rep(),
5314 Self::Guest(id) => id.rep(),
5315 Self::Transmit(id) => id.rep(),
5316 }
5317 }
5318
5319 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5323 log::trace!("waitable {self:?} join set {set:?}");
5324
5325 let old = mem::replace(&mut self.common(state)?.set, set);
5326
5327 if let Some(old) = old {
5328 match *self {
5329 Waitable::Host(id) => state.remove_child(id, old),
5330 Waitable::Guest(id) => state.remove_child(id, old),
5331 Waitable::Transmit(id) => state.remove_child(id, old),
5332 }?;
5333
5334 state.get_mut(old)?.ready.remove(self);
5335 }
5336
5337 if let Some(set) = set {
5338 match *self {
5339 Waitable::Host(id) => state.add_child(id, set),
5340 Waitable::Guest(id) => state.add_child(id, set),
5341 Waitable::Transmit(id) => state.add_child(id, set),
5342 }?;
5343
5344 if self.common(state)?.event.is_some() {
5345 self.mark_ready(state)?;
5346 }
5347 }
5348
5349 Ok(())
5350 }
5351
5352 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5354 Ok(match self {
5355 Self::Host(id) => &mut state.get_mut(*id)?.common,
5356 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5357 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5358 })
5359 }
5360
5361 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5367 if self.common(state)?.set.is_some() {
5368 bail!(Trap::WaitableSyncAndAsync);
5369 }
5370 Ok(())
5371 }
5372
5373 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5377 log::trace!("set event for {self:?}: {event:?}");
5378 self.common(state)?.event = event;
5379 self.mark_ready(state)
5380 }
5381
5382 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5384 let common = self.common(state)?;
5385 let event = common.event.take();
5386 if let Some(set) = self.common(state)?.set {
5387 state.get_mut(set)?.ready.remove(self);
5388 }
5389
5390 Ok(event)
5391 }
5392
5393 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5397 if let Some(set) = self.common(state)?.set {
5398 let set_state = state.get_mut(set)?;
5399 set_state.ready.insert(*self);
5400
5401 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5402 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5403 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5404
5405 let item = match mode {
5406 WaitMode::Caller { fiber, callee } => {
5407 let item = WorkItem::ResumeFiber {
5419 instance: state.get_mut(thread.task)?.instance,
5420 thread,
5421 fiber,
5422 };
5423
5424 if let Some(Event::Subtask {
5425 status: Status::Starting,
5426 }) = &self.common(state)?.event
5427 {
5428 state.set_switch_item(item)?;
5432 } else {
5433 if state.get_mut(callee)?.switch_item.is_some() {
5434 bail_bug!(
5435 "`GuestTask::switch_item` is already `Some(_)` when we need \
5436 to deliver a subtask status update to the caller"
5437 );
5438 }
5439 state.get_mut(callee)?.switch_item = Some(item);
5440 }
5441 None
5442 }
5443 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5444 instance: state.get_mut(thread.task)?.instance,
5445 thread,
5446 fiber,
5447 }),
5448 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5449 instance: state.get_mut(thread.task)?.instance,
5450 call: GuestCall {
5451 thread,
5452 kind: GuestCallKind::DeliverEvent {
5453 instance,
5454 set: Some(set),
5455 },
5456 },
5457 }),
5458 };
5459
5460 if let Some(item) = item {
5461 state.push_high_priority(item);
5462 }
5463 }
5464 }
5465 Ok(())
5466 }
5467
5468 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5470 match self {
5471 Self::Host(task) => {
5472 log::trace!("delete host task {task:?}");
5473 state.delete(*task)?;
5474 }
5475 Self::Guest(task) => {
5476 log::trace!("delete guest task {task:?}");
5477 let task = state.delete(*task)?;
5478
5479 debug_assert!(task.decremented_interesting_task_count);
5486 }
5487 Self::Transmit(task) => {
5488 state.delete(*task)?;
5489 }
5490 }
5491
5492 Ok(())
5493 }
5494}
5495
5496impl fmt::Debug for Waitable {
5497 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5498 match self {
5499 Self::Host(id) => write!(f, "{id:?}"),
5500 Self::Guest(id) => write!(f, "{id:?}"),
5501 Self::Transmit(id) => write!(f, "{id:?}"),
5502 }
5503 }
5504}
5505
5506#[derive(Default)]
5508struct WaitableSet {
5509 ready: BTreeSet<Waitable>,
5511 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5513 is_sync_call_set: bool,
5516}
5517
5518impl TableDebug for WaitableSet {
5519 fn type_name() -> &'static str {
5520 "WaitableSet"
5521 }
5522}
5523
5524type RawLower =
5526 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5527
5528type RawLift = Box<
5530 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5531>;
5532
5533type LiftedResult = Box<dyn Any + Send + Sync>;
5537
5538struct DummyResult;
5541
5542#[derive(Default)]
5544pub struct ConcurrentInstanceState {
5545 backpressure: u16,
5547 do_not_enter: bool,
5549 do_not_suspend: bool,
5552 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5555}
5556
5557impl ConcurrentInstanceState {
5558 pub fn pending_is_empty(&self) -> bool {
5559 self.pending.is_empty()
5560 }
5561}
5562
5563#[derive(Debug, Copy, Clone)]
5564pub(crate) enum CurrentThread {
5565 Guest(QualifiedThreadId),
5568 Host(TableId<HostTask>),
5570 GuestTask(TableId<GuestTask>),
5574 None,
5576}
5577
5578impl CurrentThread {
5579 fn guest(&self) -> Option<&QualifiedThreadId> {
5580 match self {
5581 Self::Guest(id) => Some(id),
5582 _ => None,
5583 }
5584 }
5585
5586 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5587 match self {
5588 Self::Guest(id) => Some(id.task),
5589 Self::GuestTask(id) => Some(*id),
5590 _ => None,
5591 }
5592 }
5593
5594 fn host(&self) -> Option<TableId<HostTask>> {
5595 match self {
5596 Self::Host(id) => Some(*id),
5597 _ => None,
5598 }
5599 }
5600
5601 fn is_none(&self) -> bool {
5602 matches!(self, Self::None)
5603 }
5604}
5605
5606impl From<QualifiedThreadId> for CurrentThread {
5607 fn from(id: QualifiedThreadId) -> Self {
5608 Self::Guest(id)
5609 }
5610}
5611
5612impl From<TableId<HostTask>> for CurrentThread {
5613 fn from(id: TableId<HostTask>) -> Self {
5614 Self::Host(id)
5615 }
5616}
5617
5618enum Priority {
5619 Switch,
5620 High,
5621 Low,
5622}
5623
5624pub struct ConcurrentState {
5626 unforced_current_thread: CurrentThread,
5632
5633 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5638 table: AlwaysMut<ResourceTable>,
5640 switch_item: Option<WorkItem>,
5648 high_priority: VecDeque<WorkItem>,
5650 low_priority: VecDeque<WorkItem>,
5652 suspend_reason: Option<SuspendReason>,
5656 worker: Option<StoreFiber<'static>>,
5660 worker_item: Option<WorkerItem>,
5662
5663 global_error_context_ref_counts:
5676 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5677
5678 interesting_tasks: usize,
5691
5692 interesting_tasks_empty_waker: Option<Waker>,
5696
5697 ready_for_concurrent_call_waker: Option<Waker>,
5702
5703 event_loop_running: bool,
5705}
5706
5707impl Default for ConcurrentState {
5708 fn default() -> Self {
5709 Self {
5710 unforced_current_thread: CurrentThread::None,
5711 table: AlwaysMut::new(ResourceTable::new()),
5712 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5713 switch_item: None,
5714 high_priority: VecDeque::new(),
5715 low_priority: VecDeque::new(),
5716 suspend_reason: None,
5717 worker: None,
5718 worker_item: None,
5719 global_error_context_ref_counts: BTreeMap::new(),
5720 interesting_tasks: 0,
5721 interesting_tasks_empty_waker: None,
5722 ready_for_concurrent_call_waker: None,
5723 event_loop_running: false,
5724 }
5725 }
5726}
5727
5728impl ConcurrentState {
5729 pub(crate) fn take_fibers_and_futures(
5746 &mut self,
5747 fibers: &mut Vec<StoreFiber<'static>>,
5748 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5749 ) {
5750 let mut items = Vec::new();
5751 for entry in self.table.get_mut().iter_mut() {
5752 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5753 for mode in mem::take(&mut set.waiting).into_values() {
5754 match mode {
5755 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5756 fibers.push(fiber);
5757 }
5758 WaitMode::Callback(_) => {}
5759 }
5760 }
5761 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5762 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5763 mem::replace(&mut thread.state, GuestThreadState::Completed)
5764 {
5765 fibers.push(fiber);
5766 }
5767 } else if let Some(task) = entry.downcast_mut::<GuestTask>() {
5768 if let Some(item) = task.switch_item.take() {
5769 items.push(item);
5770 }
5771 }
5772 }
5773
5774 if let Some(fiber) = self.worker.take() {
5775 fibers.push(fiber);
5776 }
5777
5778 let mut handle_item = |item| match item {
5779 WorkItem::ResumeFiber { fiber, .. } => {
5780 fibers.push(fiber);
5781 }
5782 WorkItem::PushFuture(future) => {
5783 self.futures
5784 .get_mut()
5785 .as_mut()
5786 .unwrap()
5787 .push(future.into_inner());
5788 }
5789 WorkItem::ResumeThread { .. }
5790 | WorkItem::GuestCall { .. }
5791 | WorkItem::WorkerFunction(_) => {}
5792 };
5793
5794 for item in items {
5795 handle_item(item);
5796 }
5797 if let Some(item) = self.switch_item.take() {
5798 handle_item(item);
5799 }
5800 for item in mem::take(&mut self.high_priority) {
5801 handle_item(item);
5802 }
5803 for item in mem::take(&mut self.low_priority) {
5804 handle_item(item);
5805 }
5806
5807 if let Some(them) = self.futures.get_mut().take() {
5808 futures.push(them);
5809 }
5810 }
5811
5812 #[cfg(feature = "gc")]
5813 pub(crate) fn trace_fiber_roots(
5814 &mut self,
5815 modules: &ModuleRegistry,
5816 unwind: &dyn Unwind,
5817 gc_roots_list: &mut GcRootsList,
5818 ) {
5819 let ConcurrentState {
5820 table,
5821 worker,
5822 switch_item,
5823 high_priority,
5824 low_priority,
5825
5826 futures: _,
5830
5831 worker_item: _,
5833 unforced_current_thread: _,
5834 suspend_reason: _,
5835 global_error_context_ref_counts: _,
5836 interesting_tasks: _,
5837 interesting_tasks_empty_waker: _,
5838 ready_for_concurrent_call_waker: _,
5839 event_loop_running: _,
5840 } = self;
5841
5842 for entry in table.get_mut().iter_mut() {
5843 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5844 for mode in set.waiting.values_mut() {
5845 match mode {
5846 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5847 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5848 }
5849 WaitMode::Callback(_) => {}
5850 }
5851 }
5852 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5853 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5854 &mut thread.state
5855 {
5856 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5857 }
5858 }
5859 }
5860
5861 if let Some(fiber) = worker {
5862 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5863 }
5864
5865 let mut handle_item = |item: &mut WorkItem| match item {
5866 WorkItem::ResumeFiber { fiber, .. } => {
5867 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5868 }
5869 WorkItem::PushFuture(_future) => {
5870 }
5873 WorkItem::ResumeThread { .. }
5874 | WorkItem::GuestCall { .. }
5875 | WorkItem::WorkerFunction(_) => {}
5876 };
5877
5878 if let Some(item) = switch_item {
5879 handle_item(item);
5880 }
5881 for item in high_priority {
5882 handle_item(item);
5883 }
5884 for item in low_priority {
5885 handle_item(item);
5886 }
5887 }
5888
5889 fn push<V: Send + Sync + 'static>(
5890 &mut self,
5891 value: V,
5892 ) -> Result<TableId<V>, ResourceTableError> {
5893 self.table.get_mut().push(value).map(TableId::from)
5894 }
5895
5896 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5897 self.table.get_mut().get_mut(&Resource::from(id))
5898 }
5899
5900 pub fn add_child<T: 'static, U: 'static>(
5901 &mut self,
5902 child: TableId<T>,
5903 parent: TableId<U>,
5904 ) -> Result<(), ResourceTableError> {
5905 self.table
5906 .get_mut()
5907 .add_child(Resource::from(child), Resource::from(parent))
5908 }
5909
5910 pub fn remove_child<T: 'static, U: 'static>(
5911 &mut self,
5912 child: TableId<T>,
5913 parent: TableId<U>,
5914 ) -> Result<(), ResourceTableError> {
5915 self.table
5916 .get_mut()
5917 .remove_child(Resource::from(child), Resource::from(parent))
5918 }
5919
5920 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5921 self.table.get_mut().delete(Resource::from(id))
5922 }
5923
5924 fn push_future(&mut self, future: HostTaskFuture) {
5925 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5932 }
5933
5934 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
5935 log::trace!("set switch item: {item:?}");
5936
5937 if self.switch_item.is_some() {
5938 bail_bug!("switch item already set");
5939 }
5940
5941 self.switch_item = Some(item);
5942
5943 Ok(())
5944 }
5945
5946 fn push_high_priority(&mut self, item: WorkItem) {
5947 log::trace!("push high priority: {item:?}");
5948 self.high_priority.push_front(item);
5949 }
5950
5951 fn push_low_priority(&mut self, item: WorkItem) {
5952 log::trace!("push low priority: {item:?}");
5953 self.low_priority.push_front(item);
5954 }
5955
5956 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
5957 match priority {
5958 Priority::Switch => self.set_switch_item(item)?,
5959 Priority::High => self.push_high_priority(item),
5960 Priority::Low => self.push_low_priority(item),
5961 }
5962
5963 Ok(())
5964 }
5965
5966 fn promote_instance_local_thread_work_item(
5967 &mut self,
5968 current_instance: RuntimeInstance,
5969 ) -> Result<bool> {
5970 log::trace!("promote thread work items for {current_instance:?}");
5971
5972 self.promote_work_item_matching(|item: &WorkItem| {
5973 let result = match item {
5974 WorkItem::ResumeThread { instance, .. }
5975 | WorkItem::ResumeFiber { instance, .. }
5976 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
5977 _ => false,
5978 };
5979
5980 log::trace!("candidate {item:?}: {result}");
5981 result
5982 })
5983 }
5984
5985 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
5986 self.promote_work_item_matching(|item: &WorkItem| match item {
5987 WorkItem::ResumeThread {
5988 thread: item_thread,
5989 ..
5990 }
5991 | WorkItem::GuestCall {
5992 call:
5993 GuestCall {
5994 thread: item_thread,
5995 ..
5996 },
5997 ..
5998 } => *item_thread == thread,
5999 _ => false,
6000 })
6001 }
6002
6003 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6004 where
6005 F: FnMut(&WorkItem) -> bool,
6006 {
6007 for item in mem::take(&mut self.high_priority).into_iter().rev() {
6012 if self.switch_item.is_none() && predicate(&item) {
6013 self.set_switch_item(item)?;
6014 } else {
6015 self.push_high_priority(item);
6016 }
6017 }
6018
6019 if self.switch_item.is_none() {
6020 for item in mem::take(&mut self.low_priority).into_iter().rev() {
6021 if self.switch_item.is_none() && predicate(&item) {
6022 self.set_switch_item(item)?;
6023 } else {
6024 self.push_low_priority(item);
6025 }
6026 }
6027 }
6028
6029 Ok(self.switch_item.is_some())
6030 }
6031
6032 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
6038 let (task, is_host) = (task >> 1, task & 1 == 1);
6039 if is_host {
6040 let task: TableId<HostTask> = TableId::new(task);
6041 Ok(&mut self.get_mut(task)?.call_context)
6042 } else {
6043 let task: TableId<GuestTask> = TableId::new(task);
6044 Ok(&mut self.get_mut(task)?.call_context)
6045 }
6046 }
6047
6048 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6049 match self.futures.get_mut().as_mut() {
6050 Some(f) => Ok(f),
6051 None => bail_bug!("futures field of concurrent state is currently taken"),
6052 }
6053 }
6054
6055 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6056 self.table.get_mut()
6057 }
6058
6059 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
6061 let task = match cur {
6062 CurrentThread::GuestTask(task) => task,
6063 CurrentThread::Guest(thread) => thread.task,
6064 CurrentThread::Host(id) => {
6065 return Some(CurrentThread::GuestTask(self.get_mut(id).ok()?.caller));
6066 }
6067 CurrentThread::None => return None,
6068 };
6069 let task = self.get_mut(task).ok()?;
6070 Some(match task.caller {
6071 Caller::Host { caller, .. } => caller,
6072 Caller::Guest { thread } => thread.into(),
6073 })
6074 }
6075}
6076
6077fn for_any_lower<
6080 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6081>(
6082 fun: F,
6083) -> F {
6084 fun
6085}
6086
6087fn for_any_lift<
6089 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6090>(
6091 fun: F,
6092) -> F {
6093 fun
6094}
6095
6096fn check_ambient_store(id: StoreId) {
6097 let message = "\
6098 `Future`s which depend on asynchronous component tasks, streams, or \
6099 futures to complete may only be polled from the event loop of the \
6100 store to which they belong. Please use \
6101 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6102 ";
6103 tls::try_get(|store| {
6104 let matched = match store {
6105 tls::TryGet::Some(store) => store.id() == id,
6106 tls::TryGet::Taken | tls::TryGet::None => false,
6107 };
6108
6109 if !matched {
6110 panic!("{message}")
6111 }
6112 });
6113}
6114
6115fn unpack_callback_code(code: u32) -> (u32, u32) {
6116 (code & 0xF, code >> 4)
6117}
6118
6119struct WaitableCheckParams {
6123 set: TableId<WaitableSet>,
6124 options: OptionsIndex,
6125 payload: u32,
6126}
6127
6128enum WaitableCheck {
6131 Wait,
6132 Poll,
6133}
6134
6135#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
6144pub struct GuestTaskId(TableId<GuestTask>);
6145
6146pub(crate) struct PreparedCall<R> {
6148 handle: Func,
6150 thread: QualifiedThreadId,
6152 param_count: usize,
6154 rx: oneshot::Receiver<LiftedResult>,
6157 runtime_instance: RuntimeInstance,
6159 _phantom: PhantomData<R>,
6160}
6161
6162impl<R> PreparedCall<R> {
6163 pub(crate) fn task_id(&self) -> TaskId {
6165 TaskId {
6166 task: self.thread.task,
6167 runtime_instance: self.runtime_instance,
6168 }
6169 }
6170}
6171
6172pub(crate) struct TaskId {
6174 task: TableId<GuestTask>,
6175 runtime_instance: RuntimeInstance,
6176}
6177
6178impl TaskId {
6179 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6185 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6186 let delete = if !task.already_lowered_parameters() {
6187 store.cancel_guest_subtask_without_lowered_parameters(
6188 self.runtime_instance,
6189 self.task,
6190 )?;
6191 true
6192 } else {
6193 task.host_future_state = HostFutureState::Dropped;
6194 task.ready_to_delete()
6195 };
6196 if delete {
6197 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
6198 }
6199 Ok(())
6200 }
6201}
6202
6203pub(crate) fn prepare_call<T, R>(
6209 mut store: StoreContextMut<T>,
6210 handle: Func,
6211 param_count: usize,
6212 host_future_present: bool,
6213 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6214 + Send
6215 + Sync
6216 + 'static,
6217 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6218 + Send
6219 + Sync
6220 + 'static,
6221) -> Result<PreparedCall<R>> {
6222 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6223
6224 let instance = handle.instance().id().get(store.0);
6225 let options = &instance.component().env_component().options[options];
6226 let ty = &instance.component().types()[ty];
6227 let async_typed = ty.async_;
6228 let async_lifted = raw_options.async_;
6229 let task_return_type = ty.results;
6230 let component_instance = raw_options.instance;
6231 let callback = options.callback.map(|i| instance.runtime_callback(i));
6232 let memory = options
6233 .memory()
6234 .map(|i| instance.runtime_memory(i))
6235 .map(SendSyncPtr::new);
6236 let string_encoding = options.string_encoding;
6237 let token = StoreToken::new(store.as_context_mut());
6238 let caller = store.0.current_thread()?;
6239 let state = store.0.concurrent_state_mut()?;
6240
6241 let (tx, rx) = oneshot::channel();
6242
6243 let instance = handle.instance().runtime_instance(component_instance);
6244 let thread = GuestTask::new(
6245 state,
6246 Box::new(for_any_lower(move |store, params| {
6247 lower_params(token.as_context_mut(store), params)
6248 })),
6249 LiftResult {
6250 lift: Box::new(for_any_lift(move |store, result| {
6251 lift_result(store, result)
6252 })),
6253 ty: task_return_type,
6254 memory,
6255 string_encoding,
6256 },
6257 Caller::Host {
6258 tx: Some(tx),
6259 host_future_present,
6260 caller,
6261 },
6262 callback.map(|callback| {
6263 let callback = SendSyncPtr::new(callback);
6264 let instance = handle.instance();
6265 Box::new(move |store: &mut dyn VMStore, event, handle| {
6266 let store = token.as_context_mut(store);
6267 unsafe { instance.call_callback(store, callback, event, handle) }
6270 }) as CallbackFn
6271 }),
6272 instance,
6273 async_typed,
6274 async_lifted,
6275 )?;
6276
6277 if !store.0.may_enter() {
6278 bail!(Trap::CannotEnterComponent);
6279 }
6280
6281 Ok(PreparedCall {
6282 handle,
6283 thread,
6284 param_count,
6285 runtime_instance: instance,
6286 rx,
6287 _phantom: PhantomData,
6288 })
6289}
6290
6291pub(crate) struct StagedCall<R> {
6292 store: StoreId,
6293 task: TableId<GuestTask>,
6294 rx: oneshot::Receiver<LiftedResult>,
6295 _marker: PhantomData<fn() -> R>,
6296}
6297
6298impl<R> StagedCall<R> {
6299 pub(crate) fn new<T: 'static>(
6306 mut store: StoreContextMut<T>,
6307 prepared: PreparedCall<R>,
6308 ) -> Result<StagedCall<R>> {
6309 let PreparedCall {
6310 handle,
6311 thread,
6312 param_count,
6313 rx,
6314 ..
6315 } = prepared;
6316
6317 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6318
6319 Ok(StagedCall {
6320 store: store.0.id(),
6321 task: thread.task,
6322 rx,
6323 _marker: PhantomData,
6324 })
6325 }
6326
6327 fn task(&self) -> GuestTaskId {
6328 GuestTaskId(self.task)
6329 }
6330}
6331
6332impl<R> Future for StagedCall<R>
6333where
6334 R: 'static,
6335{
6336 type Output = Result<R>;
6337
6338 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6339 check_ambient_store(self.store);
6340 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6341 Ok(r) => match r.downcast() {
6342 Ok(r) => Ok(*r),
6343 Err(_) => bail_bug!("wrong type of value produced"),
6344 },
6345 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6346 })
6347 }
6348}
6349
6350fn stage_call0<T: 'static>(
6353 store: StoreContextMut<T>,
6354 handle: Func,
6355 guest_thread: QualifiedThreadId,
6356 param_count: usize,
6357) -> Result<()> {
6358 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6359 let is_concurrent = raw_options.async_;
6360 let callback = raw_options.callback;
6361 let instance = handle.instance();
6362 let callee = handle.lifted_core_func(store.0);
6363 let post_return = raw_options
6364 .post_return
6365 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6366 let callback = callback.map(|i| {
6367 let instance = instance.id().get(store.0);
6368 SendSyncPtr::new(instance.runtime_callback(i))
6369 });
6370
6371 log::trace!("queueing call {guest_thread:?}");
6372
6373 unsafe {
6377 instance.stage_call(
6378 store,
6379 guest_thread,
6380 SendSyncPtr::new(callee),
6381 param_count,
6382 1,
6383 is_concurrent,
6384 callback,
6385 post_return.map(SendSyncPtr::new),
6386 true,
6387 )
6388 }
6389}