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>> {
1650 let mut cur = Some(self.0.current_thread()?);
1651 let state = self.0.concurrent_state_mut()?;
1652 Ok(core::iter::from_fn(move || {
1653 while let Some(t) = cur {
1654 cur = state.parent(t);
1655 if let Some(task) = t.guest_task() {
1656 return Some(GuestTaskId(task));
1657 }
1658 }
1659
1660 None
1661 }))
1662 }
1663
1664 pub(crate) async fn start_instance(
1665 &mut self,
1666 instance: ModuleInstance,
1667 ) -> Result<ModuleInstance> {
1668 let (tx, rx) = oneshot::channel();
1669 let token = StoreToken::new(self.as_context_mut());
1670 self.0.queue_task(move |store| {
1671 _ = tx.send(
1672 instance
1673 .start_raw(&mut token.as_context_mut(store))
1674 .map(|()| instance),
1675 );
1676 Ok(())
1677 })?;
1678 self.as_context_mut()
1679 .run_concurrent_trap_on_idle(async |_| {
1680 rx.await
1681 .map_err(|_| format_err!("oneshot channel canceled"))
1682 })
1683 .await??
1684 }
1685}
1686
1687pub type EnteredHostTask = Option<(TableId<HostTask>, QualifiedThreadId)>;
1693
1694impl StoreOpaque {
1695 #[inline]
1698 pub(crate) fn current_thread(&mut self) -> Result<CurrentThread> {
1699 if !self.concurrency_support() {
1701 return Ok(CurrentThread::None);
1702 }
1703
1704 if !self
1707 .vm_store_context_mut()
1708 .current_thread_mut()
1709 .is_deferred()
1710 {
1711 return Ok(self
1712 .concurrent_state_mut_already_forced_current_thread()
1713 .unforced_current_thread);
1714 }
1715
1716 self.force_deferred_current_thread()
1717 }
1718
1719 #[cold]
1722 fn force_deferred_current_thread(&mut self) -> Result<CurrentThread> {
1723 let state = self.concurrent_state_mut_without_forcing_current_thread();
1732 let id = match state.unforced_current_thread.guest_task() {
1733 Some(task) => state.get_mut(task)?.instance.instance,
1734 None => bail_bug!("deferred component-model thread with non-guest base"),
1735 };
1736
1737 let mut frames = Vec::new();
1740 let mut cur = *self.vm_store_context_mut().current_thread_mut();
1741 while let Some(ptr) = cur.as_deferred() {
1742 let deferred = unsafe { ptr.as_non_null().as_ref() };
1747 frames.push((
1748 deferred.callee_async != 0,
1749 deferred.callee_instance,
1750 deferred.saved_context,
1751 ));
1752 cur = deferred.parent;
1753 }
1754
1755 *self.vm_store_context_mut().current_thread_mut() = VMLazyThread::forced();
1759
1760 let current_context = *self.vm_store_context_mut().component_context_mut();
1763
1764 for (callee_async, callee_instance, saved_context) in frames.into_iter().rev() {
1768 *self.vm_store_context_mut().component_context_mut() = saved_context;
1772 let callee = RuntimeInstance {
1773 instance: id,
1774 index: RuntimeComponentInstanceIndex::from_u32(callee_instance),
1775 };
1776 self.enter_guest_sync_call(None, callee_async, callee)?;
1777 }
1778
1779 *self.vm_store_context_mut().component_context_mut() = current_context;
1781
1782 Ok(self
1783 .concurrent_state_mut_without_forcing_current_thread()
1784 .unforced_current_thread)
1785 }
1786
1787 fn current_guest_thread(&mut self) -> Result<QualifiedThreadId> {
1788 match self.current_thread()?.guest() {
1789 Some(id) => Ok(*id),
1790 None => bail_bug!("current thread is not a guest thread"),
1791 }
1792 }
1793
1794 fn current_host_thread(&mut self) -> Result<TableId<HostTask>> {
1795 match self.current_thread()?.host() {
1796 Some(id) => Ok(id),
1797 None => bail_bug!("current thread is not a host thread"),
1798 }
1799 }
1800
1801 fn take_pending_cancellation(&mut self) -> Result<bool> {
1804 let thread = self.current_guest_thread()?;
1805 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1806 if let Some(Event::Cancelled) = task.event {
1807 task.event.take();
1808 return Ok(true);
1809 }
1810 Ok(false)
1811 }
1812
1813 fn enter_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1814 log::trace!("enter sync-typed call {callee:?}");
1815 let state = self.instance_state(callee).concurrent_state();
1816 let old_do_not_suspend = state.do_not_suspend;
1817 state.do_not_suspend = true;
1818
1819 let thread = self.current_guest_thread()?;
1820 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1821 if thread.old_do_not_suspend.is_some() {
1822 bail_bug!("current thread already has `old_do_not_suspend` value");
1823 }
1824
1825 thread.old_do_not_suspend = Some(old_do_not_suspend);
1826
1827 Ok(())
1828 }
1829
1830 fn exit_sync_call(&mut self, callee: RuntimeInstance) -> Result<()> {
1831 log::trace!("exit sync-typed call {callee:?}");
1832 let thread = self.current_guest_thread()?;
1833 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
1834 let Some(old_do_not_suspend) = thread.old_do_not_suspend.take() else {
1835 bail_bug!("current thread missing `old_do_not_suspend` value");
1836 };
1837 let state = self.instance_state(callee).concurrent_state();
1838 state.do_not_suspend = old_do_not_suspend;
1839 Ok(())
1840 }
1841
1842 pub(crate) fn enter_guest_sync_call(
1854 &mut self,
1855 guest_caller: Option<RuntimeInstance>,
1856 callee_async_typed: bool,
1857 callee: RuntimeInstance,
1858 ) -> Result<()> {
1859 log::trace!("enter sync-lifted call {callee:?}");
1860 if !self.concurrency_support() {
1861 return self.enter_call_not_concurrent();
1862 }
1863
1864 let thread = self.current_thread()?;
1865 let state = self.concurrent_state_mut()?;
1866 let instance = if let Some(task) = thread.guest_task() {
1867 Some(state.get_mut(task)?.instance)
1868 } else {
1869 None
1870 };
1871 if guest_caller.is_some() {
1872 debug_assert_eq!(instance, guest_caller);
1873 }
1874 let guest_thread = GuestTask::new(
1875 state,
1876 Box::new(move |_, _| bail_bug!("cannot lower params in sync call")),
1877 LiftResult {
1878 lift: Box::new(move |_, _| bail_bug!("cannot lift result in sync call")),
1879 ty: TypeTupleIndex::reserved_value(),
1880 memory: None,
1881 string_encoding: StringEncoding::Utf8,
1882 },
1883 if let Some(thread) = thread.guest() {
1884 Caller::Guest { thread: *thread }
1885 } else {
1886 Caller::Host {
1887 tx: None,
1888 host_future_present: false,
1889 caller: thread,
1890 }
1891 },
1892 None,
1893 callee,
1894 callee_async_typed,
1895 true,
1896 )?;
1897
1898 Instance::from_wasmtime(self, callee.instance).add_guest_thread_to_instance_table(
1899 guest_thread.thread,
1900 self,
1901 callee.index,
1902 )?;
1903 self.set_thread(guest_thread)?;
1904
1905 if !callee_async_typed {
1906 self.enter_sync_call(callee)?;
1907 }
1908
1909 Ok(())
1910 }
1911
1912 pub(crate) fn exit_guest_sync_call(&mut self) -> Result<()> {
1920 if !self.concurrency_support() {
1921 return Ok(self.exit_call_not_concurrent());
1922 }
1923
1924 let thread = match self.current_thread()?.guest() {
1925 Some(t) => *t,
1926 None => bail_bug!("expected task when exiting"),
1927 };
1928 let task = self.concurrent_state_mut()?.get_mut(thread.task)?;
1929 let instance = task.instance;
1930
1931 let caller = match &task.caller {
1932 &Caller::Guest { thread } => thread.into(),
1933 &Caller::Host { caller, .. } => caller,
1934 };
1935 task.lift_result = None;
1936 task.exited = true;
1937 let async_typed = task.async_typed;
1938
1939 if !async_typed {
1940 self.exit_sync_call(instance)?;
1941 }
1942
1943 self.set_thread(caller)?;
1944
1945 log::trace!("exit sync-lifted call {instance:?}");
1946
1947 if async_typed {
1948 self.switch_or_trap_if_may_not_suspend(instance)?;
1953 }
1954
1955 self.cleanup_thread(thread, instance, CleanupTask::Yes)?;
1956
1957 Ok(())
1958 }
1959
1960 pub(crate) fn host_task_create(&mut self) -> Result<EnteredHostTask> {
1968 if !self.concurrency_support() {
1969 self.enter_call_not_concurrent()?;
1970 return Ok(None);
1971 }
1972 let caller = self.current_guest_thread()?;
1973 let state = self.concurrent_state_mut()?;
1974 let task = state.push(HostTask::new(caller.task, HostTaskState::CalleeStarted))?;
1975 log::trace!("new host task {task:?}");
1976 self.set_thread(task)?;
1977 Ok(Some((task, caller)))
1978 }
1979
1980 pub(crate) fn host_task_delete(&mut self, task: EnteredHostTask) -> Result<()> {
1987 match task {
1988 Some((task, caller)) => {
1989 self.set_thread(caller)?;
1990 log::trace!("delete host task {task:?}");
1991 self.concurrent_state_mut()?.delete(task)?;
1992 }
1993 None => {
1994 self.exit_call_not_concurrent();
1995 }
1996 }
1997 Ok(())
1998 }
1999
2000 fn instance_state(&mut self, instance: RuntimeInstance) -> &mut InstanceState {
2003 self.component_instance_mut(instance.instance)
2004 .instance_state(instance.index)
2005 }
2006
2007 fn set_thread(&mut self, thread: impl Into<CurrentThread>) -> Result<CurrentThread> {
2013 let thread = thread.into();
2014 let state = self.concurrent_state_mut()?;
2015 let old_thread = mem::replace(&mut state.unforced_current_thread, thread);
2016
2017 if let Some(old_thread) = old_thread.guest() {
2025 let old_context = *self.vm_store_context_mut().component_context_mut();
2026 self.concurrent_state_mut()?
2027 .get_mut(old_thread.thread)?
2028 .context = old_context;
2029 }
2030 if cfg!(debug_assertions) {
2031 *self.vm_store_context_mut().component_context_mut() =
2032 [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2033 }
2034 if let Some(thread) = thread.guest() {
2035 let thread = self.concurrent_state_mut()?.get_mut(thread.thread)?;
2036 let context = thread.context;
2037 if cfg!(debug_assertions) {
2038 thread.context = [u32::MAX; NUM_COMPONENT_CONTEXT_SLOTS];
2039 }
2040 *self.vm_store_context_mut().component_context_mut() = context;
2041 }
2042
2043 *self.vm_store_context_mut().current_thread_mut() = if thread.is_none() {
2045 VMLazyThread::none()
2046 } else {
2047 VMLazyThread::forced()
2048 };
2049
2050 Ok(old_thread)
2051 }
2052
2053 fn switch_or_trap_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<()> {
2055 if self.switch_if_may_not_suspend(instance)? {
2056 Ok(())
2057 } else {
2058 Err(Trap::CannotBlockSyncTask.into())
2059 }
2060 }
2061
2062 fn switch_if_may_not_suspend(&mut self, instance: RuntimeInstance) -> Result<bool> {
2066 self.concurrent_state_mut()?;
2070
2071 Ok(!self.concurrency_support()
2072 || !self
2073 .instance_state(instance)
2074 .concurrent_state()
2075 .do_not_suspend
2076 || self
2077 .concurrent_state_mut()?
2078 .promote_instance_local_thread_work_item(instance)?)
2079 }
2080
2081 fn enter_instance(&mut self, instance: RuntimeInstance) {
2085 log::trace!("enter {instance:?}");
2086 self.instance_state(instance)
2087 .concurrent_state()
2088 .do_not_enter = true;
2089 }
2090
2091 fn exit_instance(&mut self, instance: RuntimeInstance) -> Result<()> {
2095 log::trace!("exit {instance:?}");
2096 self.instance_state(instance)
2097 .concurrent_state()
2098 .do_not_enter = false;
2099 self.partition_pending(instance)
2100 }
2101
2102 fn partition_pending(&mut self, instance: RuntimeInstance) -> Result<()> {
2110 for (thread, kind) in
2111 mem::take(&mut self.instance_state(instance).concurrent_state().pending).into_iter()
2112 {
2113 let call = GuestCall { thread, kind };
2114 if call.is_ready(self)? {
2115 self.concurrent_state_mut()?
2116 .push_high_priority(WorkItem::GuestCall { instance, call });
2117 } else {
2118 self.instance_state(instance)
2119 .concurrent_state()
2120 .pending
2121 .insert(call.thread, call.kind);
2122 }
2123 }
2124
2125 if let Some(waker) = self
2126 .concurrent_state_mut()?
2127 .ready_for_concurrent_call_waker
2128 .take()
2129 {
2130 waker.wake();
2131 }
2132
2133 Ok(())
2134 }
2135
2136 pub(crate) fn backpressure_modify(
2138 &mut self,
2139 caller_instance: RuntimeInstance,
2140 modify: impl FnOnce(u16) -> Option<u16>,
2141 ) -> Result<()> {
2142 let state = self.instance_state(caller_instance).concurrent_state();
2143 let old = state.backpressure;
2144 let new = modify(old).ok_or_else(|| Trap::BackpressureOverflow)?;
2145 state.backpressure = new;
2146
2147 if old > 0 && new == 0 {
2148 self.partition_pending(caller_instance)?;
2151 }
2152
2153 Ok(())
2154 }
2155
2156 async fn resume_fiber(&mut self, fiber: StoreFiber<'static>) -> Result<()> {
2159 let old_thread = self.current_thread()?;
2160 log::trace!("resume_fiber: save current thread {old_thread:?}");
2161
2162 let fiber = fiber::resolve_or_release(self, fiber).await?;
2163
2164 self.set_thread(old_thread)?;
2165
2166 let state = self.concurrent_state_mut()?;
2167
2168 if let Some(ot) = old_thread.guest() {
2169 state.get_mut(ot.thread)?.state = GuestThreadState::Running;
2170 }
2171 log::trace!("resume_fiber: restore current thread {old_thread:?}");
2172
2173 if let Some(mut fiber) = fiber {
2174 log::trace!("resume_fiber: suspend reason {:?}", &state.suspend_reason);
2175 let reason = match state.suspend_reason.take() {
2177 Some(r) => r,
2178 None => bail_bug!("suspend reason missing when resuming fiber"),
2179 };
2180 match reason {
2181 SuspendReason::NeedWork => {
2182 if state.worker.is_none() {
2183 state.worker = Some(fiber);
2184 } else {
2185 fiber.dispose(self);
2186 }
2187 }
2188 SuspendReason::Yielding {
2189 thread,
2190 cancellable,
2191 } => {
2192 state.get_mut(thread.thread)?.state =
2193 GuestThreadState::Ready { fiber, cancellable };
2194 let instance = state.get_mut(thread.task)?.instance;
2195 state.push_low_priority(WorkItem::ResumeThread { instance, thread });
2196 }
2197 SuspendReason::ExplicitlySuspending { thread } => {
2198 state.get_mut(thread.thread)?.state = GuestThreadState::Suspended(fiber);
2199 }
2200 SuspendReason::Waiting { set, thread } => {
2201 let old = state
2202 .get_mut(set)?
2203 .waiting
2204 .insert(thread, WaitMode::Fiber(fiber));
2205 assert!(old.is_none());
2206 }
2207 SuspendReason::WaitingForGuestSubtask { caller, callee } => {
2208 let set = state.get_mut(caller.thread)?.sync_call_set;
2209 let old = state
2210 .get_mut(set)?
2211 .waiting
2212 .insert(caller, WaitMode::Caller { fiber, callee });
2213 assert!(old.is_none());
2214 }
2215 };
2216 } else {
2217 log::trace!("resume_fiber: fiber has exited");
2218 }
2219
2220 Ok(())
2221 }
2222
2223 fn suspend(&mut self, reason: SuspendReason) -> Result<()> {
2229 log::trace!("suspend fiber: {reason:?}");
2230
2231 let task = match &reason {
2235 SuspendReason::Yielding { thread, .. }
2236 | SuspendReason::Waiting { thread, .. }
2237 | SuspendReason::WaitingForGuestSubtask { caller: thread, .. }
2238 | SuspendReason::ExplicitlySuspending { thread } => Some(thread.task),
2239 SuspendReason::NeedWork => None,
2240 };
2241
2242 let old_guest_thread = if let Some(task) = task {
2243 let state = self.concurrent_state_mut()?;
2249 if state.switch_item.is_none() {
2250 if let Some(item) = state.get_mut(task)?.switch_item.take() {
2251 state.set_switch_item(item)?;
2252 }
2253 }
2254
2255 self.current_thread()?
2256 } else {
2257 CurrentThread::None
2258 };
2259
2260 let suspend_reason = &mut self.concurrent_state_mut()?.suspend_reason;
2261 assert!(suspend_reason.is_none());
2262 *suspend_reason = Some(reason);
2263
2264 if !self.fiber_async_state_mut().can_block() {
2267 return Err(format_err!("future dropped"));
2268 }
2269
2270 self.with_blocking(|_, cx| cx.suspend(StoreFiberYield::ReleaseStore))?;
2271
2272 if task.is_some() {
2273 self.set_thread(old_guest_thread)?;
2274 }
2275
2276 Ok(())
2277 }
2278
2279 fn wait_for_event(
2280 &mut self,
2281 caller_instance: RuntimeInstance,
2282 waitable: Waitable,
2283 reason: WaitReason,
2284 ) -> Result<()> {
2285 let caller = self.current_guest_thread()?;
2286 let state = self.concurrent_state_mut()?;
2287
2288 waitable.trap_if_in_waitable_set(state)?;
2289
2290 let set = state.get_mut(caller.thread)?.sync_call_set;
2291 waitable.join(state, Some(set))?;
2292
2293 self.switch_or_trap_if_may_not_suspend(caller_instance)?;
2294
2295 self.suspend(match reason {
2296 WaitReason::GuestSubtask(callee) => {
2297 SuspendReason::WaitingForGuestSubtask { caller, callee }
2298 }
2299 WaitReason::Other => SuspendReason::Waiting {
2300 set,
2301 thread: caller,
2302 },
2303 })?;
2304 let state = self.concurrent_state_mut()?;
2305 waitable.join(state, None)
2306 }
2307
2308 fn cleanup_thread(
2330 &mut self,
2331 guest_thread: QualifiedThreadId,
2332 runtime_instance: RuntimeInstance,
2333 cleanup_task: CleanupTask,
2334 ) -> Result<()> {
2335 let state = self.concurrent_state_mut()?;
2336 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2339 state.set_switch_item(item)?;
2340 }
2341 let thread_data = state.get_mut(guest_thread.thread)?;
2342 let sync_call_set = thread_data.sync_call_set;
2343 if let Some(guest_id) = thread_data.instance_rep {
2344 self.instance_state(runtime_instance)
2345 .thread_handle_table()
2346 .guest_thread_remove(guest_id)?;
2347 }
2348 let state = self.concurrent_state_mut()?;
2349
2350 for waitable in mem::take(&mut state.get_mut(sync_call_set)?.ready) {
2352 if let Some(Event::Subtask {
2353 status: Status::Returned | Status::ReturnCancelled,
2354 }) = waitable.common(state)?.event
2355 {
2356 waitable.delete_from(state)?;
2357 }
2358 }
2359
2360 state.delete(guest_thread.thread)?;
2361 state.delete(sync_call_set)?;
2362 let task = state.get_mut(guest_thread.task)?;
2363 task.threads.remove(&guest_thread.thread);
2364
2365 if task.threads.is_empty() && !task.returned_or_cancelled() {
2366 bail!(Trap::NoAsyncResult);
2367 }
2368 let ready_to_delete = task.ready_to_delete();
2369
2370 if !task.decremented_interesting_task_count && task.exited && task.returned_or_cancelled() {
2371 task.decremented_interesting_task_count = true;
2372
2373 debug_assert!(state.interesting_tasks > 0);
2374 state.interesting_tasks -= 1;
2375 if state.interesting_tasks == 0
2376 && let Some(waker) = state.interesting_tasks_empty_waker.take()
2377 {
2378 waker.wake();
2379 }
2380 }
2381
2382 match cleanup_task {
2383 CleanupTask::Yes => {
2384 if ready_to_delete {
2385 Waitable::Guest(guest_thread.task).delete_from(state)?;
2386 }
2387 }
2388 CleanupTask::No => {}
2389 }
2390
2391 Ok(())
2392 }
2393
2394 fn cancel_guest_subtask_without_lowered_parameters(
2407 &mut self,
2408 caller_instance: RuntimeInstance,
2409 guest_task: TableId<GuestTask>,
2410 ) -> Result<()> {
2411 let concurrent_state = self.concurrent_state_mut()?;
2412 let task = concurrent_state.get_mut(guest_task)?;
2413 assert!(!task.already_lowered_parameters());
2414 task.lower_params = None;
2418 task.lift_result = None;
2419 task.exited = true;
2420 let instance = task.instance;
2421
2422 assert_eq!(1, task.threads.len());
2425 let thread = *task.threads.iter().next().unwrap();
2426 self.cleanup_thread(
2427 QualifiedThreadId {
2428 task: guest_task,
2429 thread,
2430 },
2431 caller_instance,
2432 CleanupTask::No,
2433 )?;
2434
2435 let pending = &mut self.instance_state(instance).concurrent_state().pending;
2437 let pending_count = pending.len();
2438 pending.retain(|thread, _| thread.task != guest_task);
2439 if pending.len() == pending_count {
2441 bail!(Trap::SubtaskCancelAfterTerminal);
2442 }
2443 Ok(())
2444 }
2445
2446 pub(crate) fn current_scope_id(&mut self) -> Result<Option<u32>> {
2449 if !self.concurrency_support() {
2450 return self.current_scope_id_not_concurrent();
2451 }
2452 let (bits, is_host) = match self.current_thread()? {
2453 CurrentThread::Guest(id) => (id.task.rep(), false),
2454 CurrentThread::GuestTask(id) => (id.rep(), false),
2455 CurrentThread::Host(id) => (id.rep(), true),
2456 CurrentThread::None => return Ok(None),
2457 };
2458 assert_eq!((bits << 1) >> 1, bits);
2459 Ok(Some((bits << 1) | u32::from(is_host)))
2460 }
2461
2462 fn queue_task(
2463 &mut self,
2464 task: impl FnOnce(&mut dyn VMStore) -> Result<()> + Send + 'static,
2465 ) -> Result<()> {
2466 self.concurrent_state_mut()?
2467 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(task))));
2468 Ok(())
2469 }
2470
2471 fn any_may_not_suspend(&mut self) -> Result<bool> {
2480 Ok(self
2488 .concurrent_state_mut()?
2489 .table
2490 .get_mut()
2491 .iter_mut()
2492 .filter_map(|entry| {
2493 if let Some(task) = entry.downcast_ref::<GuestTask>() {
2494 Some(task.instance)
2495 } else {
2496 None
2497 }
2498 })
2499 .collect::<Vec<_>>()
2500 .into_iter()
2501 .any(|instance| {
2502 self.instance_state(instance)
2503 .concurrent_state()
2504 .do_not_suspend
2505 }))
2506 }
2507}
2508
2509enum CleanupTask {
2510 Yes,
2511 No,
2512}
2513
2514impl Instance {
2515 fn get_event(
2518 self,
2519 store: &mut StoreOpaque,
2520 guest_task: TableId<GuestTask>,
2521 set: Option<TableId<WaitableSet>>,
2522 cancellable: bool,
2523 ) -> Result<Option<(Event, Option<(Waitable, u32)>)>> {
2524 let state = store.concurrent_state_mut()?;
2525
2526 let event = &mut state.get_mut(guest_task)?.event;
2527 if let Some(ev) = event
2528 && (cancellable || !matches!(ev, Event::Cancelled))
2529 {
2530 log::trace!("deliver event {ev:?} to {guest_task:?}");
2531 let ev = *ev;
2532 *event = None;
2533 return Ok(Some((ev, None)));
2534 }
2535
2536 let set = match set {
2537 Some(set) => set,
2538 None => return Ok(None),
2539 };
2540 let waitable = match state.get_mut(set)?.ready.pop_first() {
2541 Some(v) => v,
2542 None => return Ok(None),
2543 };
2544
2545 let common = waitable.common(state)?;
2546 let handle = match common.handle {
2547 Some(h) => h,
2548 None => bail_bug!("handle not set when delivering event"),
2549 };
2550 let event = match common.event.take() {
2551 Some(e) => e,
2552 None => bail_bug!("event not set when delivering event"),
2553 };
2554
2555 log::trace!(
2556 "deliver event {event:?} to {guest_task:?} for {waitable:?} (handle {handle}); set {set:?}"
2557 );
2558
2559 waitable.on_delivery(store, self, event)?;
2560
2561 Ok(Some((event, Some((waitable, handle)))))
2562 }
2563
2564 fn handle_callback_code(
2570 self,
2571 store: &mut StoreOpaque,
2572 guest_thread: QualifiedThreadId,
2573 runtime_instance: RuntimeComponentInstanceIndex,
2574 code: u32,
2575 ) -> Result<()> {
2576 let (code, set) = unpack_callback_code(code);
2577
2578 log::trace!("received callback code from {guest_thread:?}: {code} (set: {set})");
2579
2580 let state = store.concurrent_state_mut()?;
2581
2582 if let Some(item) = state.get_mut(guest_thread.task)?.switch_item.take() {
2583 state.set_switch_item(item)?;
2584 }
2585
2586 let get_set = |store: &mut StoreOpaque, handle| -> Result<_> {
2587 let set = store
2588 .instance_state(self.runtime_instance(runtime_instance))
2589 .handle_table()
2590 .waitable_set_rep(handle)?;
2591
2592 Ok(TableId::<WaitableSet>::new(set))
2593 };
2594
2595 match code {
2596 callback_code::EXIT => {
2597 log::trace!("implicit thread {guest_thread:?} completed");
2598 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2599 task.exited = true;
2600 task.callback = None;
2601
2602 let runtime_instance = self.runtime_instance(runtime_instance);
2603
2604 store.switch_or_trap_if_may_not_suspend(runtime_instance)?;
2609
2610 store.cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
2611 }
2612 callback_code::YIELD => {
2613 let task = state.get_mut(guest_thread.task)?;
2614 if let Some(event) = task.event {
2619 assert!(matches!(event, Event::None | Event::Cancelled));
2620 } else {
2621 task.event = Some(Event::None);
2622 }
2623 let call = GuestCall {
2624 thread: guest_thread,
2625 kind: GuestCallKind::DeliverEvent {
2626 instance: self,
2627 set: None,
2628 },
2629 };
2630 state.push_low_priority(WorkItem::GuestCall {
2633 instance: self.runtime_instance(runtime_instance),
2634 call,
2635 });
2636 }
2637 callback_code::WAIT => {
2638 let set = get_set(store, set)?;
2639 let state = store.concurrent_state_mut()?;
2640
2641 if state.get_mut(guest_thread.task)?.event.is_some()
2642 || !state.get_mut(set)?.ready.is_empty()
2643 {
2644 state.push_high_priority(WorkItem::GuestCall {
2646 instance: self.runtime_instance(runtime_instance),
2647 call: GuestCall {
2648 thread: guest_thread,
2649 kind: GuestCallKind::DeliverEvent {
2650 instance: self,
2651 set: Some(set),
2652 },
2653 },
2654 });
2655 } else {
2656 let old = state
2664 .get_mut(guest_thread.thread)?
2665 .wake_on_cancel
2666 .replace(set);
2667 if !old.is_none() {
2668 bail_bug!("thread unexpectedly had wake_on_cancel set");
2669 }
2670 let old = state
2671 .get_mut(set)?
2672 .waiting
2673 .insert(guest_thread, WaitMode::Callback(self));
2674 if !old.is_none() {
2675 bail_bug!("set's waiting set already had this thread registered");
2676 }
2677 }
2678 }
2679 _ => bail!(Trap::UnsupportedCallbackCode),
2680 }
2681
2682 Ok(())
2683 }
2684
2685 unsafe fn stage_call<T: 'static>(
2692 self,
2693 mut store: StoreContextMut<T>,
2694 guest_thread: QualifiedThreadId,
2695 callee: SendSyncPtr<VMFuncRef>,
2696 param_count: usize,
2697 result_count: usize,
2698 async_: bool,
2699 callback: Option<SendSyncPtr<VMFuncRef>>,
2700 post_return: Option<SendSyncPtr<VMFuncRef>>,
2701 host_caller: bool,
2702 ) -> Result<()> {
2703 unsafe fn make_call<T: 'static>(
2718 store: StoreContextMut<T>,
2719 guest_thread: QualifiedThreadId,
2720 callee: SendSyncPtr<VMFuncRef>,
2721 param_count: usize,
2722 result_count: usize,
2723 ) -> impl FnOnce(&mut dyn VMStore) -> Result<[MaybeUninit<ValRaw>; MAX_FLAT_PARAMS]>
2724 + Send
2725 + Sync
2726 + 'static
2727 + use<T> {
2728 let token = StoreToken::new(store);
2729 move |store: &mut dyn VMStore| {
2730 let mut storage = [MaybeUninit::uninit(); MAX_FLAT_PARAMS];
2731
2732 store
2733 .concurrent_state_mut()?
2734 .get_mut(guest_thread.thread)?
2735 .state = GuestThreadState::Running;
2736 let task = store.concurrent_state_mut()?.get_mut(guest_thread.task)?;
2737 let lower = match task.lower_params.take() {
2738 Some(l) => l,
2739 None => bail_bug!("lower_params missing"),
2740 };
2741
2742 lower(store, &mut storage[..param_count])?;
2743
2744 let mut store = token.as_context_mut(store);
2745
2746 unsafe {
2749 crate::Func::call_unchecked_raw(
2750 &mut store,
2751 callee.as_non_null(),
2752 NonNull::new(
2753 &mut storage[..param_count.max(result_count)]
2754 as *mut [MaybeUninit<ValRaw>] as _,
2755 )
2756 .unwrap(),
2757 )?;
2758 }
2759
2760 Ok(storage)
2761 }
2762 }
2763
2764 let call = unsafe {
2768 make_call(
2769 store.as_context_mut(),
2770 guest_thread,
2771 callee,
2772 param_count,
2773 result_count,
2774 )
2775 };
2776
2777 let callee_instance = store
2778 .0
2779 .concurrent_state_mut()?
2780 .get_mut(guest_thread.task)?
2781 .instance;
2782
2783 let fun = if callback.is_some() {
2784 assert!(async_);
2785
2786 Box::new(move |store: &mut dyn VMStore| {
2787 self.add_guest_thread_to_instance_table(
2788 guest_thread.thread,
2789 store,
2790 callee_instance.index,
2791 )?;
2792 let old_thread = store.set_thread(guest_thread)?;
2793 log::trace!(
2794 "stackless call: replaced {old_thread:?} with {guest_thread:?} as current thread"
2795 );
2796
2797 store.enter_instance(callee_instance);
2798
2799 let storage = call(store)?;
2806
2807 store.exit_instance(callee_instance)?;
2808
2809 store.set_thread(old_thread)?;
2810 let state = store.concurrent_state_mut()?;
2811 if let Some(t) = old_thread.guest() {
2812 state.get_mut(t.thread)?.state = GuestThreadState::Running;
2813 }
2814 log::trace!("stackless call: restored {old_thread:?} as current thread");
2815
2816 let code = unsafe { storage[0].assume_init() }.get_i32() as u32;
2819
2820 self.handle_callback_code(store, guest_thread, callee_instance.index, code)
2821 }) as Box<dyn FnOnce(&mut dyn VMStore) -> Result<()> + Send + Sync>
2822 } else {
2823 let token = StoreToken::new(store.as_context_mut());
2824 Box::new(move |store: &mut dyn VMStore| {
2825 self.add_guest_thread_to_instance_table(
2826 guest_thread.thread,
2827 store,
2828 callee_instance.index,
2829 )?;
2830 let old_thread = store.set_thread(guest_thread)?;
2831 log::trace!(
2832 "sync/async-stackful call: replaced {old_thread:?} with {guest_thread:?} as current thread",
2833 );
2834 let flags = self.id().get(store).instance_flags(callee_instance.index);
2835
2836 let callee_async_typed = store
2837 .concurrent_state_mut()?
2838 .get_mut(guest_thread.task)?
2839 .async_typed;
2840
2841 if !async_ && callee_async_typed {
2845 store.enter_instance(callee_instance);
2846 }
2847
2848 if !callee_async_typed {
2849 store.enter_sync_call(callee_instance)?;
2850 }
2851
2852 let storage = call(store)?;
2859
2860 if !callee_async_typed {
2861 store.exit_sync_call(callee_instance)?;
2862 }
2863
2864 if !async_ {
2865 if callee_async_typed {
2871 store.exit_instance(callee_instance)?;
2872 }
2873
2874 let lift = {
2875 let state = store.concurrent_state_mut()?;
2876 if !state.get_mut(guest_thread.task)?.result.is_none() {
2877 bail_bug!("task has already produced a result");
2878 }
2879
2880 match state.get_mut(guest_thread.task)?.lift_result.take() {
2881 Some(lift) => lift,
2882 None => bail_bug!("lift_result field is missing"),
2883 }
2884 };
2885
2886 let result = (lift.lift)(store, unsafe {
2889 mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(
2890 &storage[..result_count],
2891 )
2892 })?;
2893
2894 let post_return_arg = match result_count {
2895 0 => ValRaw::i32(0),
2896 1 => unsafe { storage[0].assume_init() },
2899 _ => unreachable!(),
2900 };
2901
2902 unsafe {
2903 call_post_return(
2904 token.as_context_mut(store),
2905 post_return.map(|v| v.as_non_null()),
2906 post_return_arg,
2907 flags,
2908 )?;
2909 }
2910
2911 self.task_complete(store, guest_thread.task, result, Status::Returned)?;
2912 }
2913
2914 store.set_thread(old_thread)?;
2915
2916 store
2917 .concurrent_state_mut()?
2918 .get_mut(guest_thread.task)?
2919 .exited = true;
2920
2921 log::trace!(
2922 "clean up thread; async lifted? {async_} async typed? {callee_async_typed}"
2923 );
2924
2925 if callee_async_typed {
2926 store.switch_or_trap_if_may_not_suspend(callee_instance)?;
2931 }
2932
2933 store.cleanup_thread(guest_thread, callee_instance, CleanupTask::Yes)?;
2935 Ok(())
2936 })
2937 };
2938
2939 store.0.concurrent_state_mut()?.push_work_item(
2940 WorkItem::GuestCall {
2941 instance: callee_instance,
2942 call: GuestCall {
2943 thread: guest_thread,
2944 kind: GuestCallKind::StartImplicit(fun),
2945 },
2946 },
2947 if host_caller {
2948 Priority::High
2949 } else {
2950 Priority::Switch
2951 },
2952 )?;
2953
2954 Ok(())
2955 }
2956
2957 unsafe fn prepare_call<T: 'static>(
2970 self,
2971 mut store: StoreContextMut<T>,
2972 start: NonNull<VMFuncRef>,
2973 return_: NonNull<VMFuncRef>,
2974 caller_instance: RuntimeComponentInstanceIndex,
2975 callee_instance: RuntimeComponentInstanceIndex,
2976 task_return_type: TypeTupleIndex,
2977 callee_async_typed: bool,
2978 memory: *mut VMMemoryDefinition,
2979 string_encoding: StringEncoding,
2980 caller_info: CallerInfo,
2981 ) -> Result<()> {
2982 enum ResultInfo {
2983 Heap { results: u32 },
2984 Stack { result_count: u32 },
2985 }
2986
2987 let result_info = match &caller_info {
2988 CallerInfo::Async {
2989 has_result: true,
2990 params,
2991 } => ResultInfo::Heap {
2992 results: match params.last() {
2993 Some(r) => r.get_u32(),
2994 None => bail_bug!("retptr missing"),
2995 },
2996 },
2997 CallerInfo::Async {
2998 has_result: false, ..
2999 } => ResultInfo::Stack { result_count: 0 },
3000 CallerInfo::Sync {
3001 result_count,
3002 params,
3003 } if *result_count > u32::try_from(MAX_FLAT_RESULTS)? => ResultInfo::Heap {
3004 results: match params.last() {
3005 Some(r) => r.get_u32(),
3006 None => bail_bug!("arg ptr missing"),
3007 },
3008 },
3009 CallerInfo::Sync { result_count, .. } => ResultInfo::Stack {
3010 result_count: *result_count,
3011 },
3012 };
3013
3014 let sync_caller = matches!(caller_info, CallerInfo::Sync { .. });
3015
3016 let start = SendSyncPtr::new(start);
3020 let return_ = SendSyncPtr::new(return_);
3021 let token = StoreToken::new(store.as_context_mut());
3022 let old_thread = store.0.current_guest_thread()?;
3023 let state = store.0.concurrent_state_mut()?;
3024
3025 debug_assert_eq!(
3026 state.get_mut(old_thread.task)?.instance,
3027 self.runtime_instance(caller_instance)
3028 );
3029
3030 let guest_thread = GuestTask::new(
3031 state,
3032 Box::new(move |store, dst| {
3033 let mut store = token.as_context_mut(store);
3034 assert!(dst.len() <= MAX_FLAT_PARAMS);
3035 let mut src = [MaybeUninit::uninit(); MAX_FLAT_PARAMS + 1];
3037 let count = match caller_info {
3038 CallerInfo::Async { params, has_result } => {
3042 let params = ¶ms[..params.len() - usize::from(has_result)];
3043 for (param, src) in params.iter().zip(&mut src) {
3044 src.write(*param);
3045 }
3046 params.len()
3047 }
3048
3049 CallerInfo::Sync { params, .. } => {
3051 for (param, src) in params.iter().zip(&mut src) {
3052 src.write(*param);
3053 }
3054 params.len()
3055 }
3056 };
3057 unsafe {
3064 crate::Func::call_unchecked_raw(
3065 &mut store,
3066 start.as_non_null(),
3067 NonNull::new(
3068 &mut src[..count.max(dst.len())] as *mut [MaybeUninit<ValRaw>] as _,
3069 )
3070 .unwrap(),
3071 )?;
3072 }
3073 dst.copy_from_slice(&src[..dst.len()]);
3074 let task = store.0.current_guest_thread()?.task;
3075 let state = store.0.concurrent_state_mut()?;
3076 Waitable::Guest(task).set_event(
3077 state,
3078 Some(Event::Subtask {
3079 status: Status::Started,
3080 }),
3081 )?;
3082 Ok(())
3083 }),
3084 LiftResult {
3085 lift: Box::new(move |store, src| {
3086 let mut store = token.as_context_mut(store);
3089 let mut my_src = src.to_owned(); if let ResultInfo::Heap { results } = &result_info {
3091 my_src.push(ValRaw::u32(*results));
3092 }
3093
3094 unsafe {
3101 crate::Func::call_unchecked_raw(
3102 &mut store,
3103 return_.as_non_null(),
3104 my_src.as_mut_slice().into(),
3105 )?;
3106 }
3107
3108 let thread = store.0.current_guest_thread()?;
3109 let state = store.0.concurrent_state_mut()?;
3110 if sync_caller {
3111 state.get_mut(thread.task)?.sync_result = SyncResult::Produced(
3112 if let ResultInfo::Stack { result_count } = &result_info {
3113 match result_count {
3114 0 => None,
3115 1 => Some(my_src[0]),
3116 _ => unreachable!(),
3117 }
3118 } else {
3119 None
3120 },
3121 );
3122 }
3123 Ok(Box::new(DummyResult) as Box<dyn Any + Send + Sync>)
3124 }),
3125 ty: task_return_type,
3126 memory: NonNull::new(memory).map(SendSyncPtr::new),
3127 string_encoding,
3128 },
3129 Caller::Guest { thread: old_thread },
3130 None,
3131 self.runtime_instance(callee_instance),
3132 callee_async_typed,
3133 false,
3136 )?;
3137
3138 store.0.set_thread(guest_thread)?;
3141 log::trace!("pushed {guest_thread:?} as current thread; old thread was {old_thread:?}");
3142
3143 Ok(())
3144 }
3145
3146 unsafe fn call_callback<T>(
3151 self,
3152 mut store: StoreContextMut<T>,
3153 function: SendSyncPtr<VMFuncRef>,
3154 event: Event,
3155 handle: u32,
3156 ) -> Result<u32> {
3157 let (ordinal, result) = event.parts();
3158 let params = &mut [
3159 ValRaw::u32(ordinal),
3160 ValRaw::u32(handle),
3161 ValRaw::u32(result),
3162 ];
3163 unsafe {
3168 crate::Func::call_unchecked_raw(
3169 &mut store,
3170 function.as_non_null(),
3171 params.as_mut_slice().into(),
3172 )?;
3173 }
3174 Ok(params[0].get_u32())
3175 }
3176
3177 unsafe fn start_call<T: 'static>(
3190 self,
3191 mut store: StoreContextMut<T>,
3192 callback: *mut VMFuncRef,
3193 post_return: *mut VMFuncRef,
3194 callee: NonNull<VMFuncRef>,
3195 param_count: u32,
3196 result_count: u32,
3197 flags: u32,
3198 storage: Option<&mut [MaybeUninit<ValRaw>]>,
3199 ) -> Result<u32> {
3200 let token = StoreToken::new(store.as_context_mut());
3201 let async_caller = storage.is_none();
3202 let guest_thread = store.0.current_guest_thread()?;
3203 let state = store.0.concurrent_state_mut()?;
3204
3205 if !state.event_loop_running {
3206 bail_bug!("Instance::start_call called without a running event loop");
3207 }
3208
3209 let callee = SendSyncPtr::new(callee);
3210 let param_count = usize::try_from(param_count)?;
3211 assert!(param_count <= MAX_FLAT_PARAMS);
3212 let result_count = usize::try_from(result_count)?;
3213 assert!(result_count <= MAX_FLAT_RESULTS);
3214
3215 let task = state.get_mut(guest_thread.task)?;
3216 let callee_async_typed = task.async_typed;
3217 let callee_instance = task.instance;
3218
3219 task.async_lifted = (flags & START_FLAG_ASYNC_CALLEE) != 0;
3220
3221 if let Some(callback) = NonNull::new(callback) {
3222 let callback = SendSyncPtr::new(callback);
3226 task.callback = Some(Box::new(move |store, event, handle| {
3227 let store = token.as_context_mut(store);
3228 unsafe { self.call_callback::<T>(store, callback, event, handle) }
3229 }));
3230 }
3231
3232 let Caller::Guest { thread: caller } = &task.caller else {
3233 bail_bug!("start_call unexpectedly invoked for host->guest call");
3236 };
3237 let caller = *caller;
3238 let caller_instance = state.get_mut(caller.task)?.instance;
3239
3240 unsafe {
3242 self.stage_call(
3243 store.as_context_mut(),
3244 guest_thread,
3245 callee,
3246 param_count,
3247 result_count,
3248 (flags & START_FLAG_ASYNC_CALLEE) != 0,
3249 NonNull::new(callback).map(SendSyncPtr::new),
3250 NonNull::new(post_return).map(SendSyncPtr::new),
3251 false,
3252 )?;
3253 }
3254
3255 let old_do_not_suspend = if callee_async_typed {
3256 let state = store.0.instance_state(callee_instance).concurrent_state();
3263 let old_do_not_suspend = state.do_not_suspend;
3264 state.do_not_suspend = false;
3265 Some(old_do_not_suspend)
3266 } else {
3267 None
3268 };
3269
3270 let state = store.0.concurrent_state_mut()?;
3271
3272 let guest_waitable = Waitable::Guest(guest_thread.task);
3275 let old_set = guest_waitable.common(state)?.set;
3276 let set = state.get_mut(caller.thread)?.sync_call_set;
3277 guest_waitable.join(state, Some(set))?;
3278
3279 store.0.set_thread(CurrentThread::None)?;
3280
3281 let (status, waitable) = loop {
3297 store.0.suspend(SuspendReason::WaitingForGuestSubtask {
3298 caller,
3299 callee: guest_thread.task,
3300 })?;
3301
3302 if let Some(old_do_not_suspend) = old_do_not_suspend {
3303 store
3304 .0
3305 .instance_state(callee_instance)
3306 .concurrent_state()
3307 .do_not_suspend = old_do_not_suspend;
3308 }
3309
3310 let state = store.0.concurrent_state_mut()?;
3311
3312 log::trace!("taking event for {:?}", guest_thread.task);
3313 let event = guest_waitable.take_event(state)?;
3314 let Some(Event::Subtask { status }) = event else {
3315 bail_bug!("subtasks should only get subtask events, got {event:?}")
3316 };
3317
3318 log::trace!("status {status:?} for {:?}", guest_thread.task);
3319
3320 if status == Status::Returned {
3321 break (status, None);
3323 } else if async_caller {
3324 let handle = store
3328 .0
3329 .instance_state(caller_instance)
3330 .handle_table()
3331 .subtask_insert_guest(guest_thread.task.rep())?;
3332 store
3333 .0
3334 .concurrent_state_mut()?
3335 .get_mut(guest_thread.task)?
3336 .common
3337 .handle = Some(handle);
3338 break (status, Some(handle));
3339 } else {
3340 store.0.switch_or_trap_if_may_not_suspend(caller_instance)?;
3344 }
3345 };
3346
3347 guest_waitable.join(store.0.concurrent_state_mut()?, old_set)?;
3348
3349 store.0.set_thread(caller)?;
3351 store
3352 .0
3353 .concurrent_state_mut()?
3354 .get_mut(caller.thread)?
3355 .state = GuestThreadState::Running;
3356 log::trace!("popped current thread {guest_thread:?}; new thread is {caller:?}");
3357
3358 if let Some(storage) = storage {
3359 let state = store.0.concurrent_state_mut()?;
3363 let task = state.get_mut(guest_thread.task)?;
3364 if let Some(result) = task.sync_result.take()? {
3365 if let Some(result) = result {
3366 storage[0] = MaybeUninit::new(result);
3367 }
3368
3369 if task.exited && task.ready_to_delete() {
3370 Waitable::Guest(guest_thread.task).delete_from(state)?;
3371 }
3372 }
3373 }
3374
3375 Ok(status.pack(waitable))
3376 }
3377
3378 pub(crate) fn first_poll<T: 'static, R: Send + 'static>(
3391 self,
3392 mut store: StoreContextMut<'_, T>,
3393 host_task: EnteredHostTask,
3394 future: impl Future<Output = Result<R>> + Send + 'static,
3395 lower: impl FnOnce(StoreContextMut<T>, Option<R>, bool) -> Result<()> + Send + 'static,
3396 ) -> Result<u32> {
3397 let token = StoreToken::new(store.as_context_mut());
3398 let task = store.0.current_host_thread()?;
3399 let state = store.0.concurrent_state_mut()?;
3400
3401 let (join_handle, future) = JoinHandle::run(future);
3404 {
3405 let state = &mut state.get_mut(task)?.state;
3406 assert!(matches!(state, HostTaskState::CalleeStarted));
3407 *state = HostTaskState::CalleeRunning(join_handle);
3408 }
3409
3410 let mut future = Box::pin(future);
3411
3412 let poll = tls::set(store.0, || {
3417 future
3418 .as_mut()
3419 .poll(&mut Context::from_waker(&Waker::noop()))
3420 });
3421
3422 match poll {
3423 Poll::Ready(result) => {
3425 let result = result.transpose()?;
3426 lower(store.as_context_mut(), result, true)?;
3427 return Ok(Status::Returned.pack(None));
3428 }
3429
3430 Poll::Pending => {}
3432 }
3433
3434 let future = Box::pin(async move {
3442 let result = match future.await {
3443 Some(result) => Some(result?),
3444 None => None,
3445 };
3446 let on_complete = move |store: &mut dyn VMStore| {
3447 let mut store = token.as_context_mut(store);
3451 let old = store.0.set_thread(task)?;
3452
3453 let status = if result.is_some() {
3454 Status::Returned
3455 } else {
3456 Status::ReturnCancelled
3457 };
3458
3459 lower(store.as_context_mut(), result, false)?;
3460 let state = store.0.concurrent_state_mut()?;
3461 match &mut state.get_mut(task)?.state {
3462 HostTaskState::CalleeDone { .. } => {}
3465
3466 other => *other = HostTaskState::CalleeDone { cancelled: false },
3468 }
3469 Waitable::Host(task).set_event(state, Some(Event::Subtask { status }))?;
3470
3471 store.0.set_thread(old)?;
3472 Ok(())
3473 };
3474
3475 tls::get(move |store| {
3480 store
3481 .concurrent_state_mut()?
3482 .push_high_priority(WorkItem::WorkerFunction(AlwaysMut::new(Box::new(
3483 on_complete,
3484 ))));
3485 Ok(())
3486 })
3487 });
3488
3489 let caller = match host_task {
3492 Some(pair) => pair.1,
3493 None => bail_bug!("host task wasn't created but should have been"),
3494 };
3495 let state = store.0.concurrent_state_mut()?;
3496 state.push_future(future);
3497 let instance = state.get_mut(caller.task)?.instance;
3498 let handle = store
3499 .0
3500 .instance_state(instance)
3501 .handle_table()
3502 .subtask_insert_host(task.rep())?;
3503 store.0.concurrent_state_mut()?.get_mut(task)?.common.handle = Some(handle);
3504 log::trace!("assign {task:?} handle {handle} for {caller:?} instance {instance:?}");
3505
3506 store.0.set_thread(caller)?;
3510 Ok(Status::Started.pack(Some(handle)))
3511 }
3512
3513 pub(crate) fn task_return(
3516 self,
3517 store: &mut dyn VMStore,
3518 ty: TypeTupleIndex,
3519 options: OptionsIndex,
3520 storage: &[ValRaw],
3521 ) -> Result<()> {
3522 let guest_thread = store.current_guest_thread()?;
3523 let state = store.concurrent_state_mut()?;
3524 let lift = state
3525 .get_mut(guest_thread.task)?
3526 .lift_result
3527 .take()
3528 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3529 if !state.get_mut(guest_thread.task)?.result.is_none() {
3530 bail_bug!("task result unexpectedly already set");
3531 }
3532
3533 let CanonicalOptions {
3534 string_encoding,
3535 data_model,
3536 ..
3537 } = &self.id().get(store).component().env_component().options[options];
3538
3539 let invalid = ty != lift.ty
3540 || string_encoding != &lift.string_encoding
3541 || match data_model {
3542 CanonicalOptionsDataModel::LinearMemory(opts) => match opts.memory {
3543 Some(memory) => {
3544 let expected = lift.memory.map(|v| v.as_ptr()).unwrap_or(ptr::null_mut());
3545 let actual = self.id().get(store).runtime_memory(memory);
3546 expected != actual.as_ptr()
3547 }
3548 None => false,
3551 },
3552 CanonicalOptionsDataModel::Gc { .. } => true,
3554 };
3555
3556 if invalid {
3557 bail!(Trap::TaskReturnInvalid);
3558 }
3559
3560 log::trace!("task.return for {guest_thread:?}");
3561
3562 let result = (lift.lift)(store, storage)?;
3563 self.task_complete(store, guest_thread.task, result, Status::Returned)
3564 }
3565
3566 pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> {
3568 let guest_thread = store.current_guest_thread()?;
3569 let state = store.concurrent_state_mut()?;
3570 let task = state.get_mut(guest_thread.task)?;
3571 if !task.cancel_sent {
3572 bail!(Trap::TaskCancelNotCancelled);
3573 }
3574 _ = task
3575 .lift_result
3576 .take()
3577 .ok_or_else(|| Trap::TaskCancelOrReturnTwice)?;
3578
3579 if !task.result.is_none() {
3580 bail_bug!("task result should not bet set yet");
3581 }
3582
3583 log::trace!("task.cancel for {guest_thread:?}");
3584
3585 self.task_complete(
3586 store,
3587 guest_thread.task,
3588 Box::new(DummyResult),
3589 Status::ReturnCancelled,
3590 )
3591 }
3592
3593 fn task_complete(
3599 self,
3600 store: &mut StoreOpaque,
3601 guest_task: TableId<GuestTask>,
3602 result: Box<dyn Any + Send + Sync>,
3603 status: Status,
3604 ) -> Result<()> {
3605 store
3606 .component_resource_tables(Some(self))?
3607 .validate_scope_exit()?;
3608
3609 let state = store.concurrent_state_mut()?;
3610 let task = state.get_mut(guest_task)?;
3611
3612 if let Caller::Host { tx, .. } = &mut task.caller {
3613 if let Some(tx) = tx.take() {
3614 _ = tx.send(result);
3615 }
3616 } else {
3617 task.result = Some(result);
3618 Waitable::Guest(guest_task).set_event(state, Some(Event::Subtask { status }))?;
3619 }
3620
3621 Ok(())
3622 }
3623
3624 pub(crate) fn waitable_set_new(
3626 self,
3627 store: &mut StoreOpaque,
3628 caller_instance: RuntimeComponentInstanceIndex,
3629 ) -> Result<u32> {
3630 let set = store.concurrent_state_mut()?.push(WaitableSet::default())?;
3631 let handle = store
3632 .instance_state(self.runtime_instance(caller_instance))
3633 .handle_table()
3634 .waitable_set_insert(set.rep())?;
3635 log::trace!("new waitable set {set:?} (handle {handle})");
3636 Ok(handle)
3637 }
3638
3639 pub(crate) fn waitable_set_drop(
3641 self,
3642 store: &mut StoreOpaque,
3643 caller_instance: RuntimeComponentInstanceIndex,
3644 set: u32,
3645 ) -> Result<()> {
3646 let rep = store
3647 .instance_state(self.runtime_instance(caller_instance))
3648 .handle_table()
3649 .waitable_set_remove(set)?;
3650
3651 log::trace!("drop waitable set {rep} (handle {set})");
3652
3653 if !store
3657 .concurrent_state_mut()?
3658 .get_mut(TableId::<WaitableSet>::new(rep))?
3659 .waiting
3660 .is_empty()
3661 {
3662 bail!(Trap::WaitableSetDropHasWaiters);
3663 }
3664
3665 store
3666 .concurrent_state_mut()?
3667 .delete(TableId::<WaitableSet>::new(rep))?;
3668
3669 Ok(())
3670 }
3671
3672 pub(crate) fn waitable_join(
3674 self,
3675 store: &mut StoreOpaque,
3676 caller_instance: RuntimeComponentInstanceIndex,
3677 waitable_handle: u32,
3678 set_handle: u32,
3679 ) -> Result<()> {
3680 let mut instance = self.id().get_mut(store);
3681 let waitable =
3682 Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?;
3683
3684 let set = if set_handle == 0 {
3685 None
3686 } else {
3687 let set = instance.instance_states().0[caller_instance]
3688 .handle_table()
3689 .waitable_set_rep(set_handle)?;
3690
3691 let state = store.concurrent_state_mut()?;
3692 if let Some(old) = waitable.common(state)?.set
3693 && state.get_mut(old)?.is_sync_call_set
3694 {
3695 bail!(Trap::WaitableSyncAndAsync);
3696 }
3697
3698 Some(TableId::<WaitableSet>::new(set))
3699 };
3700
3701 log::trace!(
3702 "waitable {waitable:?} (handle {waitable_handle}) join set {set:?} (handle {set_handle})",
3703 );
3704
3705 waitable.join(store.concurrent_state_mut()?, set)
3706 }
3707
3708 pub(crate) fn subtask_drop(
3710 self,
3711 store: &mut StoreOpaque,
3712 caller_instance: RuntimeComponentInstanceIndex,
3713 task_id: u32,
3714 ) -> Result<()> {
3715 self.waitable_join(store, caller_instance, task_id, 0)?;
3716
3717 let (rep, is_host) = store
3718 .instance_state(self.runtime_instance(caller_instance))
3719 .handle_table()
3720 .subtask_remove(task_id)?;
3721
3722 let concurrent_state = store.concurrent_state_mut()?;
3723 let (waitable, delete) = if is_host {
3724 let id = TableId::<HostTask>::new(rep);
3725 let task = concurrent_state.get_mut(id)?;
3726 match &task.state {
3727 HostTaskState::CalleeRunning(_) => bail!(Trap::SubtaskDropNotResolved),
3728 HostTaskState::CalleeDone { .. } => {}
3729 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
3730 bail_bug!("invalid state for callee in `subtask.drop`")
3731 }
3732 }
3733 (Waitable::Host(id), true)
3734 } else {
3735 let id = TableId::<GuestTask>::new(rep);
3736 let task = concurrent_state.get_mut(id)?;
3737 if task.lift_result.is_some() {
3738 bail!(Trap::SubtaskDropNotResolved);
3739 }
3740 (
3741 Waitable::Guest(id),
3742 concurrent_state.get_mut(id)?.ready_to_delete(),
3743 )
3744 };
3745
3746 waitable.common(concurrent_state)?.handle = None;
3747
3748 if waitable.take_event(concurrent_state)?.is_some() {
3751 bail!(Trap::SubtaskDropNotResolved);
3752 }
3753
3754 if delete {
3755 waitable.delete_from(concurrent_state)?;
3756 }
3757
3758 log::trace!("subtask_drop {waitable:?} (handle {task_id})");
3759 Ok(())
3760 }
3761
3762 pub(crate) fn waitable_set_wait(
3764 self,
3765 store: &mut StoreOpaque,
3766 options: OptionsIndex,
3767 set: u32,
3768 payload: u32,
3769 ) -> Result<u32> {
3770 let &CanonicalOptions {
3771 cancellable,
3772 instance: caller_instance,
3773 ..
3774 } = &self.id().get(store).component().env_component().options[options];
3775 let caller = self.runtime_instance(caller_instance);
3776 let rep = store
3777 .instance_state(self.runtime_instance(caller_instance))
3778 .handle_table()
3779 .waitable_set_rep(set)?;
3780
3781 self.waitable_check(
3782 store,
3783 caller,
3784 cancellable,
3785 WaitableCheck::Wait,
3786 WaitableCheckParams {
3787 set: TableId::new(rep),
3788 options,
3789 payload,
3790 },
3791 )
3792 }
3793
3794 pub(crate) fn waitable_set_poll(
3796 self,
3797 store: &mut StoreOpaque,
3798 options: OptionsIndex,
3799 set: u32,
3800 payload: u32,
3801 ) -> Result<u32> {
3802 let &CanonicalOptions {
3803 cancellable,
3804 instance: caller_instance,
3805 ..
3806 } = &self.id().get(store).component().env_component().options[options];
3807 let caller = self.runtime_instance(caller_instance);
3808 let rep = store
3809 .instance_state(caller)
3810 .handle_table()
3811 .waitable_set_rep(set)?;
3812
3813 self.waitable_check(
3814 store,
3815 caller,
3816 cancellable,
3817 WaitableCheck::Poll,
3818 WaitableCheckParams {
3819 set: TableId::new(rep),
3820 options,
3821 payload,
3822 },
3823 )
3824 }
3825
3826 pub(crate) fn thread_index(&self, store: &mut dyn VMStore) -> Result<u32> {
3828 let thread_id = store.current_guest_thread()?.thread;
3829 match store
3830 .concurrent_state_mut()?
3831 .get_mut(thread_id)?
3832 .instance_rep
3833 {
3834 Some(r) => Ok(r),
3835 None => bail_bug!("thread should have instance_rep by now"),
3836 }
3837 }
3838
3839 pub(crate) fn thread_new_indirect<T: 'static>(
3841 self,
3842 mut store: StoreContextMut<T>,
3843 runtime_instance: RuntimeComponentInstanceIndex,
3844 _func_ty_idx: TypeFuncIndex, start_func_table_idx: RuntimeTableIndex,
3846 start_func_idx: u32,
3847 context: i32,
3848 ) -> Result<u32> {
3849 log::trace!("creating new thread");
3850
3851 let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []);
3852 let (instance, registry) = self.id().get_mut_and_registry(store.0);
3853 let callee = instance
3854 .index_runtime_func_table(registry, start_func_table_idx, start_func_idx as u64)?
3855 .ok_or_else(|| Trap::ThreadNewIndirectUninitialized)?;
3856 if callee.type_index(store.0) != start_func_ty.type_index() {
3857 bail!(Trap::ThreadNewIndirectInvalidType);
3858 }
3859
3860 let token = StoreToken::new(store.as_context_mut());
3861 let start_func = Box::new(
3862 move |store: &mut dyn VMStore, guest_thread: QualifiedThreadId| -> Result<()> {
3863 let old_thread = store.set_thread(guest_thread)?;
3864 log::trace!(
3865 "thread start: replaced {old_thread:?} with {guest_thread:?} as current thread"
3866 );
3867
3868 let mut store = token.as_context_mut(store);
3869 let mut params = [ValRaw::i32(context)];
3870 unsafe { callee.call_unchecked(store.as_context_mut(), &mut params)? };
3873
3874 store.0.set_thread(old_thread)?;
3875
3876 let runtime_instance = self.runtime_instance(runtime_instance);
3877
3878 store
3881 .0
3882 .switch_or_trap_if_may_not_suspend(runtime_instance)?;
3883
3884 store
3885 .0
3886 .cleanup_thread(guest_thread, runtime_instance, CleanupTask::Yes)?;
3887
3888 log::trace!("explicit thread {guest_thread:?} completed");
3889 let state = store.0.concurrent_state_mut()?;
3890 if let Some(t) = old_thread.guest() {
3891 state.get_mut(t.thread)?.state = GuestThreadState::Running;
3892 }
3893 log::trace!("thread start: restored {old_thread:?} as current thread");
3894
3895 Ok(())
3896 },
3897 );
3898
3899 let current_thread = store.0.current_guest_thread()?;
3900 let state = store.0.concurrent_state_mut()?;
3901 let parent_task = current_thread.task;
3902
3903 let new_thread = GuestThread::new_explicit(state, parent_task, start_func)?;
3904 let thread_id = state.push(new_thread)?;
3905 state.get_mut(parent_task)?.threads.insert(thread_id);
3906
3907 log::trace!("new thread with id {thread_id:?} created");
3908
3909 self.add_guest_thread_to_instance_table(thread_id, store.0, runtime_instance)
3910 }
3911
3912 pub(crate) fn resume_thread(
3913 self,
3914 store: &mut StoreOpaque,
3915 runtime_instance: RuntimeComponentInstanceIndex,
3916 thread_idx: u32,
3917 how: ResumeThread,
3918 ) -> Result<bool> {
3919 let thread_id =
3920 GuestThread::from_instance(self.id().get_mut(store), runtime_instance, thread_idx)?;
3921 let state = store.concurrent_state_mut()?;
3922 let guest_thread = QualifiedThreadId::qualify(state, thread_id)?;
3923
3924 if store.current_guest_thread()? == guest_thread {
3925 bail!(Trap::CannotResumeThread);
3926 }
3927
3928 let state = store.concurrent_state_mut()?;
3929 let thread = state.get_mut(guest_thread.thread)?;
3930 let priority = match how {
3931 ResumeThread::Promote | ResumeThread::Resume => Priority::Switch,
3932 ResumeThread::ResumeLater => Priority::Low,
3933 };
3934
3935 match (&how, &thread.state) {
3936 (ResumeThread::Promote, GuestThreadState::Ready { .. }) => {}
3938 (ResumeThread::Promote, _) => return Ok(false),
3939
3940 (
3943 ResumeThread::Resume | ResumeThread::ResumeLater,
3944 GuestThreadState::NotStartedExplicit(_) | GuestThreadState::Suspended(_),
3945 ) => {}
3946 (ResumeThread::Resume | ResumeThread::ResumeLater, _) => {
3947 bail!(Trap::CannotResumeThread)
3948 }
3949 }
3950
3951 match mem::replace(&mut thread.state, GuestThreadState::Running) {
3952 GuestThreadState::NotStartedExplicit(start_func) => {
3953 log::trace!("starting thread {guest_thread:?}");
3954 let guest_call = WorkItem::GuestCall {
3955 instance: self.runtime_instance(runtime_instance),
3956 call: GuestCall {
3957 thread: guest_thread,
3958 kind: GuestCallKind::StartExplicit(Box::new(move |store| {
3959 start_func(store, guest_thread)
3960 })),
3961 },
3962 };
3963 store
3964 .concurrent_state_mut()?
3965 .push_work_item(guest_call, priority)?;
3966 }
3967 GuestThreadState::Suspended(fiber) => {
3968 log::trace!("resuming thread {thread_id:?} that was suspended");
3969 store.concurrent_state_mut()?.push_work_item(
3970 WorkItem::ResumeFiber {
3971 instance: self.runtime_instance(runtime_instance),
3972 thread: guest_thread,
3973 fiber,
3974 },
3975 priority,
3976 )?;
3977 }
3978 GuestThreadState::Ready { fiber, cancellable } => {
3979 log::trace!("resuming thread {thread_id:?} that was ready");
3980 thread.state = GuestThreadState::Ready { fiber, cancellable };
3981 store
3982 .concurrent_state_mut()?
3983 .promote_thread_work_item(guest_thread)?;
3984 }
3985 other @ (GuestThreadState::NotStartedImplicit
3986 | GuestThreadState::Running
3987 | GuestThreadState::Completed) => {
3988 thread.state = other;
3989 }
3990 }
3991 Ok(true)
3992 }
3993
3994 fn add_guest_thread_to_instance_table(
3995 self,
3996 thread_id: TableId<GuestThread>,
3997 store: &mut StoreOpaque,
3998 runtime_instance: RuntimeComponentInstanceIndex,
3999 ) -> Result<u32> {
4000 let guest_id = store
4001 .instance_state(self.runtime_instance(runtime_instance))
4002 .thread_handle_table()
4003 .guest_thread_insert(thread_id.rep())?;
4004 store
4005 .concurrent_state_mut()?
4006 .get_mut(thread_id)?
4007 .instance_rep = Some(guest_id);
4008 Ok(guest_id)
4009 }
4010
4011 pub(crate) fn suspension_intrinsic(
4015 self,
4016 store: &mut StoreOpaque,
4017 caller: RuntimeComponentInstanceIndex,
4018 cancellable: bool,
4019 yielding: bool,
4020 to_thread: SuspensionTarget,
4021 ) -> Result<WaitResult> {
4022 if cancellable && store.take_pending_cancellation()? {
4024 return Ok(WaitResult::Cancelled);
4025 }
4026
4027 let check_suspend = match to_thread {
4028 SuspensionTarget::Promote(thread) => {
4029 !self.resume_thread(store, caller, thread, ResumeThread::Promote)?
4030 }
4031 SuspensionTarget::Resume(thread) => {
4032 if !self.resume_thread(store, caller, thread, ResumeThread::Resume)? {
4033 bail_bug!(
4034 "`resume_thread` should only ever return false \
4035 when `ResumeThread::Promote` is passed to it"
4036 );
4037 }
4038 false
4039 }
4040 SuspensionTarget::None => true,
4041 };
4042
4043 if check_suspend && !store.switch_if_may_not_suspend(self.runtime_instance(caller))? {
4044 return if yielding {
4045 Ok(WaitResult::Completed)
4046 } else {
4047 Err(Trap::CannotBlockSyncTask.into())
4048 };
4049 }
4050
4051 let guest_thread = store.current_guest_thread()?;
4052
4053 let reason = if yielding {
4054 SuspendReason::Yielding {
4055 thread: guest_thread,
4056 cancellable,
4057 }
4058 } else {
4059 SuspendReason::ExplicitlySuspending {
4060 thread: guest_thread,
4061 }
4062 };
4063
4064 store.suspend(reason)?;
4065
4066 if cancellable && store.take_pending_cancellation()? {
4067 Ok(WaitResult::Cancelled)
4068 } else {
4069 Ok(WaitResult::Completed)
4070 }
4071 }
4072
4073 fn waitable_check(
4075 self,
4076 store: &mut StoreOpaque,
4077 caller: RuntimeInstance,
4078 cancellable: bool,
4079 check: WaitableCheck,
4080 params: WaitableCheckParams,
4081 ) -> Result<u32> {
4082 let guest_thread = store.current_guest_thread()?;
4083
4084 log::trace!("waitable check for {guest_thread:?}; set {:?}", params.set);
4085
4086 let state = store.concurrent_state_mut()?;
4087 let task = state.get_mut(guest_thread.task)?;
4088
4089 match &check {
4092 WaitableCheck::Wait => {
4093 let set = params.set;
4094
4095 if (task.event.is_none()
4096 || (matches!(task.event, Some(Event::Cancelled)) && !cancellable))
4097 && state.get_mut(set)?.ready.is_empty()
4098 {
4099 store.switch_or_trap_if_may_not_suspend(caller)?;
4100
4101 if cancellable {
4102 let old = store
4103 .concurrent_state_mut()?
4104 .get_mut(guest_thread.thread)?
4105 .wake_on_cancel
4106 .replace(set);
4107 if !old.is_none() {
4108 bail_bug!("thread unexpectedly in a prior wake_on_cancel set");
4109 }
4110 }
4111
4112 store.suspend(SuspendReason::Waiting {
4113 set,
4114 thread: guest_thread,
4115 })?;
4116 }
4117 }
4118 WaitableCheck::Poll => {}
4119 }
4120
4121 log::trace!(
4122 "waitable check for {guest_thread:?}; set {:?}, part two",
4123 params.set
4124 );
4125
4126 let event = self.get_event(store, guest_thread.task, Some(params.set), cancellable)?;
4128
4129 let (ordinal, handle, result) = match &check {
4130 WaitableCheck::Wait => {
4131 let (event, waitable) = match event {
4132 Some(p) => p,
4133 None => bail_bug!("event expected to be present"),
4134 };
4135 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4136 let (ordinal, result) = event.parts();
4137 (ordinal, handle, result)
4138 }
4139 WaitableCheck::Poll => {
4140 if let Some((event, waitable)) = event {
4141 let handle = waitable.map(|(_, v)| v).unwrap_or(0);
4142 let (ordinal, result) = event.parts();
4143 (ordinal, handle, result)
4144 } else {
4145 log::trace!(
4146 "no events ready to deliver via waitable-set.poll to {:?}; set {:?}",
4147 guest_thread.task,
4148 params.set
4149 );
4150 let (ordinal, result) = Event::None.parts();
4151 (ordinal, 0, result)
4152 }
4153 }
4154 };
4155 let memory = self.options_memory_mut(store, params.options);
4156 let ptr = crate::component::func::validate_inbounds_dynamic(
4157 &CanonicalAbiInfo::POINTER_PAIR,
4158 memory,
4159 &ValRaw::u32(params.payload),
4160 )?;
4161 memory[ptr + 0..][..4].copy_from_slice(&handle.to_le_bytes());
4162 memory[ptr + 4..][..4].copy_from_slice(&result.to_le_bytes());
4163 Ok(ordinal)
4164 }
4165
4166 pub(crate) fn subtask_cancel(
4168 self,
4169 store: &mut StoreOpaque,
4170 caller_instance: RuntimeComponentInstanceIndex,
4171 async_: bool,
4172 task_id: u32,
4173 ) -> Result<u32> {
4174 let (rep, is_host) = store
4175 .instance_state(self.runtime_instance(caller_instance))
4176 .handle_table()
4177 .subtask_rep(task_id)?;
4178 let waitable = if is_host {
4179 Waitable::Host(TableId::<HostTask>::new(rep))
4180 } else {
4181 Waitable::Guest(TableId::<GuestTask>::new(rep))
4182 };
4183 let concurrent_state = store.concurrent_state_mut()?;
4184
4185 log::trace!("subtask_cancel {waitable:?} (handle {task_id}; async {async_})");
4186
4187 waitable.trap_if_in_waitable_set(concurrent_state)?;
4188
4189 let needs_block;
4190 if let Waitable::Host(host_task) = waitable {
4191 let state = &mut concurrent_state.get_mut(host_task)?.state;
4192 match mem::replace(state, HostTaskState::CalleeDone { cancelled: true }) {
4193 HostTaskState::CalleeRunning(handle) => {
4200 handle.abort();
4201 needs_block = true;
4202 }
4203
4204 HostTaskState::CalleeDone { cancelled } => {
4207 if cancelled {
4208 bail!(Trap::SubtaskCancelAfterTerminal);
4209 } else {
4210 needs_block = false;
4213 }
4214 }
4215
4216 HostTaskState::CalleeStarted | HostTaskState::CalleeFinished(_) => {
4219 bail_bug!("invalid states for host callee")
4220 }
4221 }
4222 } else {
4223 let guest_task = TableId::<GuestTask>::new(rep);
4224 let task = concurrent_state.get_mut(guest_task)?;
4225 if !task.already_lowered_parameters() {
4226 store.cancel_guest_subtask_without_lowered_parameters(
4227 self.runtime_instance(caller_instance),
4228 guest_task,
4229 )?;
4230 return Ok(Status::StartCancelled as u32);
4231 } else if !task.returned_or_cancelled() {
4232 task.cancel_sent = true;
4235 task.event = Some(Event::Cancelled);
4240 let runtime_instance = task.instance;
4241 for thread in task.threads.clone() {
4242 let thread = QualifiedThreadId {
4243 task: guest_task,
4244 thread,
4245 };
4246 let thread_mut = concurrent_state.get_mut(thread.thread)?;
4247
4248 let yield_ = |store: &mut StoreOpaque| {
4249 let state = store.instance_state(runtime_instance).concurrent_state();
4254 let old_do_not_suspend = state.do_not_suspend;
4255 state.do_not_suspend = false;
4256
4257 let caller = store.current_guest_thread()?;
4258
4259 let state = store.concurrent_state_mut()?;
4264 let set = state.get_mut(caller.thread)?.sync_call_set;
4265 waitable.join(state, Some(set))?;
4266
4267 store.suspend(SuspendReason::Yielding {
4268 thread: caller,
4269 cancellable: false,
4270 })?;
4271
4272 let state = store.concurrent_state_mut()?;
4273 waitable.join(state, None)?;
4274
4275 store
4276 .instance_state(runtime_instance)
4277 .concurrent_state()
4278 .do_not_suspend = old_do_not_suspend;
4279
4280 Ok::<(), crate::Error>(())
4281 };
4282
4283 if let Some(set) = thread_mut.wake_on_cancel.take() {
4284 let item = match concurrent_state.get_mut(set)?.waiting.remove(&thread) {
4286 Some(WaitMode::Fiber(fiber)) => WorkItem::ResumeFiber {
4287 instance: runtime_instance,
4288 thread,
4289 fiber,
4290 },
4291 Some(WaitMode::Callback(instance)) => WorkItem::GuestCall {
4292 instance: runtime_instance,
4293 call: GuestCall {
4294 thread,
4295 kind: GuestCallKind::DeliverEvent {
4296 instance,
4297 set: None,
4298 },
4299 },
4300 },
4301 Some(WaitMode::Caller { .. }) => {
4302 bail_bug!("unexpected `WaitMode::Caller` in wake_on_cancel set")
4303 }
4304 None => bail_bug!("thread not present in wake_on_cancel set"),
4305 };
4306 concurrent_state.set_switch_item(item)?;
4307
4308 yield_(store)?;
4309
4310 break;
4311 } else if let GuestThreadState::Ready {
4312 cancellable: true, ..
4313 } = &thread_mut.state
4314 {
4315 if !concurrent_state.promote_thread_work_item(thread)? {
4318 bail_bug!("a ready thread should have been promotable");
4319 }
4320
4321 yield_(store)?;
4322
4323 break;
4324 }
4325 }
4326
4327 needs_block = !store
4330 .concurrent_state_mut()?
4331 .get_mut(guest_task)?
4332 .returned_or_cancelled()
4333 } else {
4334 needs_block = false;
4335 }
4336 };
4337
4338 if needs_block {
4342 if async_ {
4343 return Ok(BLOCKED);
4344 }
4345
4346 store.wait_for_event(
4349 self.runtime_instance(caller_instance),
4350 waitable,
4351 if is_host {
4352 WaitReason::Other
4353 } else {
4354 WaitReason::GuestSubtask(TableId::<GuestTask>::new(rep))
4355 },
4356 )?;
4357
4358 }
4360
4361 let event = waitable.take_event(store.concurrent_state_mut()?)?;
4362 if let Some(Event::Subtask {
4363 status: status @ (Status::Returned | Status::ReturnCancelled),
4364 }) = event
4365 {
4366 Ok(status as u32)
4367 } else {
4368 bail!(Trap::SubtaskCancelAfterTerminal);
4369 }
4370 }
4371}
4372
4373pub trait VMComponentAsyncStore {
4381 unsafe fn prepare_call(
4387 &mut self,
4388 instance: Instance,
4389 memory: *mut VMMemoryDefinition,
4390 start: NonNull<VMFuncRef>,
4391 return_: NonNull<VMFuncRef>,
4392 caller_instance: RuntimeComponentInstanceIndex,
4393 callee_instance: RuntimeComponentInstanceIndex,
4394 task_return_type: TypeTupleIndex,
4395 callee_async: bool,
4396 string_encoding: StringEncoding,
4397 result_count: u32,
4398 storage: *mut ValRaw,
4399 storage_len: usize,
4400 ) -> Result<()>;
4401
4402 unsafe fn sync_start(
4405 &mut self,
4406 instance: Instance,
4407 callback: *mut VMFuncRef,
4408 callee: NonNull<VMFuncRef>,
4409 param_count: u32,
4410 storage: *mut MaybeUninit<ValRaw>,
4411 storage_len: usize,
4412 ) -> Result<()>;
4413
4414 unsafe fn async_start(
4417 &mut self,
4418 instance: Instance,
4419 callback: *mut VMFuncRef,
4420 post_return: *mut VMFuncRef,
4421 callee: NonNull<VMFuncRef>,
4422 param_count: u32,
4423 result_count: u32,
4424 flags: u32,
4425 ) -> Result<u32>;
4426
4427 fn future_write(
4429 &mut self,
4430 instance: Instance,
4431 caller: RuntimeComponentInstanceIndex,
4432 ty: TypeFutureTableIndex,
4433 options: OptionsIndex,
4434 future: u32,
4435 address: u32,
4436 ) -> Result<u32>;
4437
4438 fn future_read(
4440 &mut self,
4441 instance: Instance,
4442 caller: RuntimeComponentInstanceIndex,
4443 ty: TypeFutureTableIndex,
4444 options: OptionsIndex,
4445 future: u32,
4446 address: u32,
4447 ) -> Result<u32>;
4448
4449 fn future_drop_writable(
4451 &mut self,
4452 instance: Instance,
4453 ty: TypeFutureTableIndex,
4454 writer: u32,
4455 ) -> Result<()>;
4456
4457 fn stream_write(
4459 &mut self,
4460 instance: Instance,
4461 caller: RuntimeComponentInstanceIndex,
4462 ty: TypeStreamTableIndex,
4463 options: OptionsIndex,
4464 stream: u32,
4465 address: u32,
4466 count: u32,
4467 ) -> Result<u32>;
4468
4469 fn stream_read(
4471 &mut self,
4472 instance: Instance,
4473 caller: RuntimeComponentInstanceIndex,
4474 ty: TypeStreamTableIndex,
4475 options: OptionsIndex,
4476 stream: u32,
4477 address: u32,
4478 count: u32,
4479 ) -> Result<u32>;
4480
4481 fn flat_stream_write(
4484 &mut self,
4485 instance: Instance,
4486 caller: RuntimeComponentInstanceIndex,
4487 ty: TypeStreamTableIndex,
4488 options: OptionsIndex,
4489 payload_size: u32,
4490 payload_align: u32,
4491 stream: u32,
4492 address: u32,
4493 count: u32,
4494 ) -> Result<u32>;
4495
4496 fn flat_stream_read(
4499 &mut self,
4500 instance: Instance,
4501 caller: RuntimeComponentInstanceIndex,
4502 ty: TypeStreamTableIndex,
4503 options: OptionsIndex,
4504 payload_size: u32,
4505 payload_align: u32,
4506 stream: u32,
4507 address: u32,
4508 count: u32,
4509 ) -> Result<u32>;
4510
4511 fn stream_drop_writable(
4513 &mut self,
4514 instance: Instance,
4515 ty: TypeStreamTableIndex,
4516 writer: u32,
4517 ) -> Result<()>;
4518
4519 fn error_context_debug_message(
4521 &mut self,
4522 instance: Instance,
4523 ty: TypeComponentLocalErrorContextTableIndex,
4524 options: OptionsIndex,
4525 err_ctx_handle: u32,
4526 debug_msg_address: u32,
4527 ) -> Result<()>;
4528
4529 fn thread_new_indirect(
4531 &mut self,
4532 instance: Instance,
4533 caller: RuntimeComponentInstanceIndex,
4534 func_ty_idx: TypeFuncIndex,
4535 start_func_table_idx: RuntimeTableIndex,
4536 start_func_idx: u32,
4537 context: i32,
4538 ) -> Result<u32>;
4539}
4540
4541impl<T: 'static> VMComponentAsyncStore for StoreInner<T> {
4543 unsafe fn prepare_call(
4544 &mut self,
4545 instance: Instance,
4546 memory: *mut VMMemoryDefinition,
4547 start: NonNull<VMFuncRef>,
4548 return_: NonNull<VMFuncRef>,
4549 caller_instance: RuntimeComponentInstanceIndex,
4550 callee_instance: RuntimeComponentInstanceIndex,
4551 task_return_type: TypeTupleIndex,
4552 callee_async: bool,
4553 string_encoding: StringEncoding,
4554 result_count_or_max_if_async: u32,
4555 storage: *mut ValRaw,
4556 storage_len: usize,
4557 ) -> Result<()> {
4558 let params = unsafe { core::slice::from_raw_parts(storage, storage_len) }.to_vec();
4562
4563 unsafe {
4564 instance.prepare_call(
4565 StoreContextMut(self),
4566 start,
4567 return_,
4568 caller_instance,
4569 callee_instance,
4570 task_return_type,
4571 callee_async,
4572 memory,
4573 string_encoding,
4574 match result_count_or_max_if_async {
4575 PREPARE_ASYNC_NO_RESULT => CallerInfo::Async {
4576 params,
4577 has_result: false,
4578 },
4579 PREPARE_ASYNC_WITH_RESULT => CallerInfo::Async {
4580 params,
4581 has_result: true,
4582 },
4583 result_count => CallerInfo::Sync {
4584 params,
4585 result_count,
4586 },
4587 },
4588 )
4589 }
4590 }
4591
4592 unsafe fn sync_start(
4593 &mut self,
4594 instance: Instance,
4595 callback: *mut VMFuncRef,
4596 callee: NonNull<VMFuncRef>,
4597 param_count: u32,
4598 storage: *mut MaybeUninit<ValRaw>,
4599 storage_len: usize,
4600 ) -> Result<()> {
4601 unsafe {
4602 instance
4603 .start_call(
4604 StoreContextMut(self),
4605 callback,
4606 ptr::null_mut(),
4607 callee,
4608 param_count,
4609 1,
4610 START_FLAG_ASYNC_CALLEE,
4611 Some(core::slice::from_raw_parts_mut(storage, storage_len)),
4615 )
4616 .map(drop)
4617 }
4618 }
4619
4620 unsafe fn async_start(
4621 &mut self,
4622 instance: Instance,
4623 callback: *mut VMFuncRef,
4624 post_return: *mut VMFuncRef,
4625 callee: NonNull<VMFuncRef>,
4626 param_count: u32,
4627 result_count: u32,
4628 flags: u32,
4629 ) -> Result<u32> {
4630 unsafe {
4631 instance.start_call(
4632 StoreContextMut(self),
4633 callback,
4634 post_return,
4635 callee,
4636 param_count,
4637 result_count,
4638 flags,
4639 None,
4640 )
4641 }
4642 }
4643
4644 fn future_write(
4645 &mut self,
4646 instance: Instance,
4647 caller: RuntimeComponentInstanceIndex,
4648 ty: TypeFutureTableIndex,
4649 options: OptionsIndex,
4650 future: u32,
4651 address: u32,
4652 ) -> Result<u32> {
4653 instance
4654 .guest_write(
4655 StoreContextMut(self),
4656 caller,
4657 TransmitIndex::Future(ty),
4658 options,
4659 None,
4660 future,
4661 address,
4662 1,
4663 )
4664 .map(|result| result.encode())
4665 }
4666
4667 fn future_read(
4668 &mut self,
4669 instance: Instance,
4670 caller: RuntimeComponentInstanceIndex,
4671 ty: TypeFutureTableIndex,
4672 options: OptionsIndex,
4673 future: u32,
4674 address: u32,
4675 ) -> Result<u32> {
4676 instance
4677 .guest_read(
4678 StoreContextMut(self),
4679 caller,
4680 TransmitIndex::Future(ty),
4681 options,
4682 None,
4683 future,
4684 address,
4685 1,
4686 )
4687 .map(|result| result.encode())
4688 }
4689
4690 fn stream_write(
4691 &mut self,
4692 instance: Instance,
4693 caller: RuntimeComponentInstanceIndex,
4694 ty: TypeStreamTableIndex,
4695 options: OptionsIndex,
4696 stream: u32,
4697 address: u32,
4698 count: u32,
4699 ) -> Result<u32> {
4700 instance
4701 .guest_write(
4702 StoreContextMut(self),
4703 caller,
4704 TransmitIndex::Stream(ty),
4705 options,
4706 None,
4707 stream,
4708 address,
4709 count,
4710 )
4711 .map(|result| result.encode())
4712 }
4713
4714 fn stream_read(
4715 &mut self,
4716 instance: Instance,
4717 caller: RuntimeComponentInstanceIndex,
4718 ty: TypeStreamTableIndex,
4719 options: OptionsIndex,
4720 stream: u32,
4721 address: u32,
4722 count: u32,
4723 ) -> Result<u32> {
4724 instance
4725 .guest_read(
4726 StoreContextMut(self),
4727 caller,
4728 TransmitIndex::Stream(ty),
4729 options,
4730 None,
4731 stream,
4732 address,
4733 count,
4734 )
4735 .map(|result| result.encode())
4736 }
4737
4738 fn future_drop_writable(
4739 &mut self,
4740 instance: Instance,
4741 ty: TypeFutureTableIndex,
4742 writer: u32,
4743 ) -> Result<()> {
4744 instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer)
4745 }
4746
4747 fn flat_stream_write(
4748 &mut self,
4749 instance: Instance,
4750 caller: RuntimeComponentInstanceIndex,
4751 ty: TypeStreamTableIndex,
4752 options: OptionsIndex,
4753 payload_size: u32,
4754 payload_align: u32,
4755 stream: u32,
4756 address: u32,
4757 count: u32,
4758 ) -> Result<u32> {
4759 instance
4760 .guest_write(
4761 StoreContextMut(self),
4762 caller,
4763 TransmitIndex::Stream(ty),
4764 options,
4765 Some(FlatAbi {
4766 size: payload_size,
4767 align: payload_align,
4768 }),
4769 stream,
4770 address,
4771 count,
4772 )
4773 .map(|result| result.encode())
4774 }
4775
4776 fn flat_stream_read(
4777 &mut self,
4778 instance: Instance,
4779 caller: RuntimeComponentInstanceIndex,
4780 ty: TypeStreamTableIndex,
4781 options: OptionsIndex,
4782 payload_size: u32,
4783 payload_align: u32,
4784 stream: u32,
4785 address: u32,
4786 count: u32,
4787 ) -> Result<u32> {
4788 instance
4789 .guest_read(
4790 StoreContextMut(self),
4791 caller,
4792 TransmitIndex::Stream(ty),
4793 options,
4794 Some(FlatAbi {
4795 size: payload_size,
4796 align: payload_align,
4797 }),
4798 stream,
4799 address,
4800 count,
4801 )
4802 .map(|result| result.encode())
4803 }
4804
4805 fn stream_drop_writable(
4806 &mut self,
4807 instance: Instance,
4808 ty: TypeStreamTableIndex,
4809 writer: u32,
4810 ) -> Result<()> {
4811 instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer)
4812 }
4813
4814 fn error_context_debug_message(
4815 &mut self,
4816 instance: Instance,
4817 ty: TypeComponentLocalErrorContextTableIndex,
4818 options: OptionsIndex,
4819 err_ctx_handle: u32,
4820 debug_msg_address: u32,
4821 ) -> Result<()> {
4822 instance.error_context_debug_message(
4823 StoreContextMut(self),
4824 ty,
4825 options,
4826 err_ctx_handle,
4827 debug_msg_address,
4828 )
4829 }
4830
4831 fn thread_new_indirect(
4832 &mut self,
4833 instance: Instance,
4834 caller: RuntimeComponentInstanceIndex,
4835 func_ty_idx: TypeFuncIndex,
4836 start_func_table_idx: RuntimeTableIndex,
4837 start_func_idx: u32,
4838 context: i32,
4839 ) -> Result<u32> {
4840 instance.thread_new_indirect(
4841 StoreContextMut(self),
4842 caller,
4843 func_ty_idx,
4844 start_func_table_idx,
4845 start_func_idx,
4846 context,
4847 )
4848 }
4849}
4850
4851type HostTaskFuture = Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>>;
4852
4853pub(crate) struct HostTask {
4857 common: WaitableCommon,
4858
4859 caller: TableId<GuestTask>,
4866
4867 call_context: CallContext,
4870
4871 state: HostTaskState,
4872}
4873
4874enum HostTaskState {
4875 CalleeStarted,
4880
4881 CalleeRunning(JoinHandle),
4886
4887 CalleeFinished(LiftedResult),
4891
4892 CalleeDone { cancelled: bool },
4895}
4896
4897impl HostTask {
4898 fn new(caller: TableId<GuestTask>, state: HostTaskState) -> Self {
4899 Self {
4900 common: WaitableCommon::default(),
4901 call_context: CallContext::default(),
4902 caller,
4903 state,
4904 }
4905 }
4906}
4907
4908impl TableDebug for HostTask {
4909 fn type_name() -> &'static str {
4910 "HostTask"
4911 }
4912}
4913
4914type CallbackFn = Box<dyn Fn(&mut dyn VMStore, Event, u32) -> Result<u32> + Send + Sync + 'static>;
4915
4916enum Caller {
4918 Host {
4920 tx: Option<oneshot::Sender<LiftedResult>>,
4922 host_future_present: bool,
4925 caller: CurrentThread,
4929 },
4930 Guest {
4932 thread: QualifiedThreadId,
4934 },
4935}
4936
4937struct LiftResult {
4940 lift: RawLift,
4941 ty: TypeTupleIndex,
4942 memory: Option<SendSyncPtr<VMMemoryDefinition>>,
4943 string_encoding: StringEncoding,
4944}
4945
4946#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
4951pub(crate) struct QualifiedThreadId {
4952 task: TableId<GuestTask>,
4953 thread: TableId<GuestThread>,
4954}
4955
4956impl QualifiedThreadId {
4957 fn qualify(
4958 state: &mut ConcurrentState,
4959 thread: TableId<GuestThread>,
4960 ) -> Result<QualifiedThreadId> {
4961 Ok(QualifiedThreadId {
4962 task: state.get_mut(thread)?.parent_task,
4963 thread,
4964 })
4965 }
4966}
4967
4968impl fmt::Debug for QualifiedThreadId {
4969 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4970 f.debug_tuple("QualifiedThreadId")
4971 .field(&self.task.rep())
4972 .field(&self.thread.rep())
4973 .finish()
4974 }
4975}
4976
4977enum GuestThreadState {
4978 NotStartedImplicit,
4979 NotStartedExplicit(
4980 Box<dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync>,
4981 ),
4982 Running,
4983 Suspended(StoreFiber<'static>),
4984 Ready {
4985 fiber: StoreFiber<'static>,
4986 cancellable: bool,
4987 },
4988 Completed,
4989}
4990
4991impl fmt::Debug for GuestThreadState {
4992 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4993 match self {
4994 Self::NotStartedImplicit => f.debug_tuple("NotStartedImplicit").finish(),
4995 Self::NotStartedExplicit(_) => f.debug_tuple("NotStartedExplicit").finish(),
4996 Self::Running => f.debug_tuple("Running").finish(),
4997 Self::Suspended(_) => f.debug_tuple("Suspended").finish(),
4998 Self::Ready { cancellable, .. } => f
4999 .debug_struct("Ready")
5000 .field("cancellable", cancellable)
5001 .finish(),
5002 Self::Completed => f.debug_tuple("Completed").finish(),
5003 }
5004 }
5005}
5006
5007pub struct GuestThread {
5008 context: [u32; NUM_COMPONENT_CONTEXT_SLOTS],
5011 parent_task: TableId<GuestTask>,
5013 wake_on_cancel: Option<TableId<WaitableSet>>,
5016 state: GuestThreadState,
5018 instance_rep: Option<u32>,
5021 sync_call_set: TableId<WaitableSet>,
5023 old_do_not_suspend: Option<bool>,
5026}
5027
5028impl GuestThread {
5029 fn from_instance(
5032 state: Pin<&mut ComponentInstance>,
5033 caller_instance: RuntimeComponentInstanceIndex,
5034 guest_thread: u32,
5035 ) -> Result<TableId<Self>> {
5036 let rep = state.instance_states().0[caller_instance]
5037 .thread_handle_table()
5038 .guest_thread_rep(guest_thread)?;
5039 Ok(TableId::new(rep))
5040 }
5041
5042 fn new_implicit(state: &mut ConcurrentState, parent_task: TableId<GuestTask>) -> Result<Self> {
5043 let sync_call_set = state.push(WaitableSet {
5044 is_sync_call_set: true,
5045 ..WaitableSet::default()
5046 })?;
5047 Ok(Self {
5048 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5049 parent_task,
5050 wake_on_cancel: None,
5051 state: GuestThreadState::NotStartedImplicit,
5052 instance_rep: None,
5053 sync_call_set,
5054 old_do_not_suspend: None,
5055 })
5056 }
5057
5058 fn new_explicit(
5059 state: &mut ConcurrentState,
5060 parent_task: TableId<GuestTask>,
5061 start_func: Box<
5062 dyn FnOnce(&mut dyn VMStore, QualifiedThreadId) -> Result<()> + Send + Sync,
5063 >,
5064 ) -> Result<Self> {
5065 let sync_call_set = state.push(WaitableSet {
5066 is_sync_call_set: true,
5067 ..WaitableSet::default()
5068 })?;
5069 Ok(Self {
5070 context: [0; NUM_COMPONENT_CONTEXT_SLOTS],
5071 parent_task,
5072 wake_on_cancel: None,
5073 state: GuestThreadState::NotStartedExplicit(start_func),
5074 instance_rep: None,
5075 sync_call_set,
5076 old_do_not_suspend: None,
5077 })
5078 }
5079}
5080
5081impl TableDebug for GuestThread {
5082 fn type_name() -> &'static str {
5083 "GuestThread"
5084 }
5085}
5086
5087enum SyncResult {
5088 NotProduced,
5089 Produced(Option<ValRaw>),
5090 Taken,
5091}
5092
5093impl SyncResult {
5094 fn take(&mut self) -> Result<Option<Option<ValRaw>>> {
5095 Ok(match mem::replace(self, SyncResult::Taken) {
5096 SyncResult::NotProduced => None,
5097 SyncResult::Produced(val) => Some(val),
5098 SyncResult::Taken => {
5099 bail_bug!("attempted to take a synchronous result that was already taken")
5100 }
5101 })
5102 }
5103}
5104
5105#[derive(Debug)]
5106enum HostFutureState {
5107 NotApplicable,
5108 Live,
5109 Dropped,
5110}
5111
5112pub(crate) struct GuestTask {
5114 common: WaitableCommon,
5116 lower_params: Option<RawLower>,
5118 lift_result: Option<LiftResult>,
5120 result: Option<LiftedResult>,
5123 callback: Option<CallbackFn>,
5126 caller: Caller,
5128 call_context: CallContext,
5133 sync_result: SyncResult,
5136 cancel_sent: bool,
5139 starting_sent: bool,
5142 instance: RuntimeInstance,
5149 event: Option<Event>,
5152 exited: bool,
5154 threads: HashSet<TableId<GuestThread>>,
5156 host_future_state: HostFutureState,
5159 async_typed: bool,
5162 async_lifted: bool,
5165
5166 decremented_interesting_task_count: bool,
5167 switch_item: Option<WorkItem>,
5168}
5169
5170impl GuestTask {
5171 fn already_lowered_parameters(&self) -> bool {
5172 self.lower_params.is_none()
5174 }
5175
5176 fn returned_or_cancelled(&self) -> bool {
5177 self.lift_result.is_none()
5179 }
5180
5181 fn ready_to_delete(&self) -> bool {
5182 let threads_completed = self.threads.is_empty();
5183 let has_sync_result = matches!(self.sync_result, SyncResult::Produced(_));
5184 let pending_completion_event = matches!(
5185 self.common.event,
5186 Some(Event::Subtask {
5187 status: Status::Returned | Status::ReturnCancelled
5188 })
5189 );
5190 let ready = threads_completed
5191 && !has_sync_result
5192 && !pending_completion_event
5193 && !matches!(self.host_future_state, HostFutureState::Live);
5194 log::trace!(
5195 "ready to delete? {ready} (threads_completed: {}, has_sync_result: {}, pending_completion_event: {}, host_future_state: {:?})",
5196 threads_completed,
5197 has_sync_result,
5198 pending_completion_event,
5199 self.host_future_state
5200 );
5201 ready
5202 }
5203
5204 fn new(
5205 state: &mut ConcurrentState,
5206 lower_params: RawLower,
5207 lift_result: LiftResult,
5208 caller: Caller,
5209 callback: Option<CallbackFn>,
5210 instance: RuntimeInstance,
5211 async_typed: bool,
5212 async_lifted: bool,
5213 ) -> Result<QualifiedThreadId> {
5214 let host_future_state = match &caller {
5215 Caller::Guest { .. } => HostFutureState::NotApplicable,
5216 Caller::Host {
5217 host_future_present,
5218 ..
5219 } => {
5220 if *host_future_present {
5221 HostFutureState::Live
5222 } else {
5223 HostFutureState::NotApplicable
5224 }
5225 }
5226 };
5227 let task = state.push(Self {
5228 common: WaitableCommon::default(),
5229 lower_params: Some(lower_params),
5230 lift_result: Some(lift_result),
5231 result: None,
5232 callback,
5233 caller,
5234 call_context: CallContext::default(),
5235 sync_result: SyncResult::NotProduced,
5236 cancel_sent: false,
5237 starting_sent: false,
5238 instance,
5239 event: None,
5240 exited: false,
5241 threads: HashSet::new(),
5242 host_future_state,
5243 async_typed,
5244 async_lifted,
5245 decremented_interesting_task_count: false,
5246 switch_item: None,
5247 })?;
5248 let new_thread = GuestThread::new_implicit(state, task)?;
5249 let thread = state.push(new_thread)?;
5250 state.get_mut(task)?.threads.insert(thread);
5251 state.interesting_tasks += 1;
5252 let thread = QualifiedThreadId { task, thread };
5253 log::trace!("new implicit thread {thread:?} for instance {instance:?}");
5254 Ok(thread)
5255 }
5256}
5257
5258impl TableDebug for GuestTask {
5259 fn type_name() -> &'static str {
5260 "GuestTask"
5261 }
5262}
5263
5264#[derive(Default)]
5266struct WaitableCommon {
5267 event: Option<Event>,
5269 set: Option<TableId<WaitableSet>>,
5271 handle: Option<u32>,
5273}
5274
5275#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
5277enum Waitable {
5278 Host(TableId<HostTask>),
5280 Guest(TableId<GuestTask>),
5282 Transmit(TableId<TransmitHandle>),
5284}
5285
5286impl Waitable {
5287 fn from_instance(
5290 state: Pin<&mut ComponentInstance>,
5291 caller_instance: RuntimeComponentInstanceIndex,
5292 waitable: u32,
5293 ) -> Result<Self> {
5294 use crate::runtime::vm::component::Waitable;
5295
5296 let (waitable, kind) = state.instance_states().0[caller_instance]
5297 .handle_table()
5298 .waitable_rep(waitable)?;
5299
5300 Ok(match kind {
5301 Waitable::Subtask { is_host: true } => Self::Host(TableId::new(waitable)),
5302 Waitable::Subtask { is_host: false } => Self::Guest(TableId::new(waitable)),
5303 Waitable::Stream | Waitable::Future => Self::Transmit(TableId::new(waitable)),
5304 })
5305 }
5306
5307 fn rep(&self) -> u32 {
5309 match self {
5310 Self::Host(id) => id.rep(),
5311 Self::Guest(id) => id.rep(),
5312 Self::Transmit(id) => id.rep(),
5313 }
5314 }
5315
5316 fn join(&self, state: &mut ConcurrentState, set: Option<TableId<WaitableSet>>) -> Result<()> {
5320 log::trace!("waitable {self:?} join set {set:?}");
5321
5322 let old = mem::replace(&mut self.common(state)?.set, set);
5323
5324 if let Some(old) = old {
5325 match *self {
5326 Waitable::Host(id) => state.remove_child(id, old),
5327 Waitable::Guest(id) => state.remove_child(id, old),
5328 Waitable::Transmit(id) => state.remove_child(id, old),
5329 }?;
5330
5331 state.get_mut(old)?.ready.remove(self);
5332 }
5333
5334 if let Some(set) = set {
5335 match *self {
5336 Waitable::Host(id) => state.add_child(id, set),
5337 Waitable::Guest(id) => state.add_child(id, set),
5338 Waitable::Transmit(id) => state.add_child(id, set),
5339 }?;
5340
5341 if self.common(state)?.event.is_some() {
5342 self.mark_ready(state)?;
5343 }
5344 }
5345
5346 Ok(())
5347 }
5348
5349 fn common<'a>(&self, state: &'a mut ConcurrentState) -> Result<&'a mut WaitableCommon> {
5351 Ok(match self {
5352 Self::Host(id) => &mut state.get_mut(*id)?.common,
5353 Self::Guest(id) => &mut state.get_mut(*id)?.common,
5354 Self::Transmit(id) => &mut state.get_mut(*id)?.common,
5355 })
5356 }
5357
5358 fn trap_if_in_waitable_set(&self, state: &mut ConcurrentState) -> Result<()> {
5364 if self.common(state)?.set.is_some() {
5365 bail!(Trap::WaitableSyncAndAsync);
5366 }
5367 Ok(())
5368 }
5369
5370 fn set_event(&self, state: &mut ConcurrentState, event: Option<Event>) -> Result<()> {
5374 log::trace!("set event for {self:?}: {event:?}");
5375 self.common(state)?.event = event;
5376 self.mark_ready(state)
5377 }
5378
5379 fn take_event(&self, state: &mut ConcurrentState) -> Result<Option<Event>> {
5381 let common = self.common(state)?;
5382 let event = common.event.take();
5383 if let Some(set) = self.common(state)?.set {
5384 state.get_mut(set)?.ready.remove(self);
5385 }
5386
5387 Ok(event)
5388 }
5389
5390 fn mark_ready(&self, state: &mut ConcurrentState) -> Result<()> {
5394 if let Some(set) = self.common(state)?.set {
5395 let set_state = state.get_mut(set)?;
5396 set_state.ready.insert(*self);
5397
5398 if let Some((thread, mode)) = set_state.waiting.pop_first() {
5399 let wake_on_cancel = state.get_mut(thread.thread)?.wake_on_cancel.take();
5400 assert!(wake_on_cancel.is_none() || wake_on_cancel == Some(set));
5401
5402 let item = match mode {
5403 WaitMode::Caller { fiber, callee } => {
5404 let item = WorkItem::ResumeFiber {
5416 instance: state.get_mut(thread.task)?.instance,
5417 thread,
5418 fiber,
5419 };
5420
5421 if let Some(Event::Subtask {
5422 status: Status::Starting,
5423 }) = &self.common(state)?.event
5424 {
5425 state.set_switch_item(item)?;
5429 } else {
5430 if state.get_mut(callee)?.switch_item.is_some() {
5431 bail_bug!(
5432 "`GuestTask::switch_item` is already `Some(_)` when we need \
5433 to deliver a subtask status update to the caller"
5434 );
5435 }
5436 state.get_mut(callee)?.switch_item = Some(item);
5437 }
5438 None
5439 }
5440 WaitMode::Fiber(fiber) => Some(WorkItem::ResumeFiber {
5441 instance: state.get_mut(thread.task)?.instance,
5442 thread,
5443 fiber,
5444 }),
5445 WaitMode::Callback(instance) => Some(WorkItem::GuestCall {
5446 instance: state.get_mut(thread.task)?.instance,
5447 call: GuestCall {
5448 thread,
5449 kind: GuestCallKind::DeliverEvent {
5450 instance,
5451 set: Some(set),
5452 },
5453 },
5454 }),
5455 };
5456
5457 if let Some(item) = item {
5458 state.push_high_priority(item);
5459 }
5460 }
5461 }
5462 Ok(())
5463 }
5464
5465 fn delete_from(&self, state: &mut ConcurrentState) -> Result<()> {
5467 match self {
5468 Self::Host(task) => {
5469 log::trace!("delete host task {task:?}");
5470 state.delete(*task)?;
5471 }
5472 Self::Guest(task) => {
5473 log::trace!("delete guest task {task:?}");
5474 let task = state.delete(*task)?;
5475
5476 debug_assert!(task.decremented_interesting_task_count);
5483 }
5484 Self::Transmit(task) => {
5485 state.delete(*task)?;
5486 }
5487 }
5488
5489 Ok(())
5490 }
5491}
5492
5493impl fmt::Debug for Waitable {
5494 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5495 match self {
5496 Self::Host(id) => write!(f, "{id:?}"),
5497 Self::Guest(id) => write!(f, "{id:?}"),
5498 Self::Transmit(id) => write!(f, "{id:?}"),
5499 }
5500 }
5501}
5502
5503#[derive(Default)]
5505struct WaitableSet {
5506 ready: BTreeSet<Waitable>,
5508 waiting: BTreeMap<QualifiedThreadId, WaitMode>,
5510 is_sync_call_set: bool,
5513}
5514
5515impl TableDebug for WaitableSet {
5516 fn type_name() -> &'static str {
5517 "WaitableSet"
5518 }
5519}
5520
5521type RawLower =
5523 Box<dyn FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync>;
5524
5525type RawLift = Box<
5527 dyn FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
5528>;
5529
5530type LiftedResult = Box<dyn Any + Send + Sync>;
5534
5535struct DummyResult;
5538
5539#[derive(Default)]
5541pub struct ConcurrentInstanceState {
5542 backpressure: u16,
5544 do_not_enter: bool,
5546 do_not_suspend: bool,
5549 pending: BTreeMap<QualifiedThreadId, GuestCallKind>,
5552}
5553
5554impl ConcurrentInstanceState {
5555 pub fn pending_is_empty(&self) -> bool {
5556 self.pending.is_empty()
5557 }
5558}
5559
5560#[derive(Debug, Copy, Clone)]
5561pub(crate) enum CurrentThread {
5562 Guest(QualifiedThreadId),
5565 Host(TableId<HostTask>),
5567 GuestTask(TableId<GuestTask>),
5571 None,
5573}
5574
5575impl CurrentThread {
5576 fn guest(&self) -> Option<&QualifiedThreadId> {
5577 match self {
5578 Self::Guest(id) => Some(id),
5579 _ => None,
5580 }
5581 }
5582
5583 fn guest_task(&self) -> Option<TableId<GuestTask>> {
5584 match self {
5585 Self::Guest(id) => Some(id.task),
5586 Self::GuestTask(id) => Some(*id),
5587 _ => None,
5588 }
5589 }
5590
5591 fn host(&self) -> Option<TableId<HostTask>> {
5592 match self {
5593 Self::Host(id) => Some(*id),
5594 _ => None,
5595 }
5596 }
5597
5598 fn is_none(&self) -> bool {
5599 matches!(self, Self::None)
5600 }
5601}
5602
5603impl From<QualifiedThreadId> for CurrentThread {
5604 fn from(id: QualifiedThreadId) -> Self {
5605 Self::Guest(id)
5606 }
5607}
5608
5609impl From<TableId<HostTask>> for CurrentThread {
5610 fn from(id: TableId<HostTask>) -> Self {
5611 Self::Host(id)
5612 }
5613}
5614
5615enum Priority {
5616 Switch,
5617 High,
5618 Low,
5619}
5620
5621pub struct ConcurrentState {
5623 unforced_current_thread: CurrentThread,
5629
5630 futures: AlwaysMut<Option<FuturesUnordered<HostTaskFuture>>>,
5635 table: AlwaysMut<ResourceTable>,
5637 switch_item: Option<WorkItem>,
5645 high_priority: VecDeque<WorkItem>,
5647 low_priority: VecDeque<WorkItem>,
5649 suspend_reason: Option<SuspendReason>,
5653 worker: Option<StoreFiber<'static>>,
5657 worker_item: Option<WorkerItem>,
5659
5660 global_error_context_ref_counts:
5673 BTreeMap<TypeComponentGlobalErrorContextTableIndex, GlobalErrorContextRefCount>,
5674
5675 interesting_tasks: usize,
5688
5689 interesting_tasks_empty_waker: Option<Waker>,
5693
5694 ready_for_concurrent_call_waker: Option<Waker>,
5699
5700 event_loop_running: bool,
5702}
5703
5704impl Default for ConcurrentState {
5705 fn default() -> Self {
5706 Self {
5707 unforced_current_thread: CurrentThread::None,
5708 table: AlwaysMut::new(ResourceTable::new()),
5709 futures: AlwaysMut::new(Some(FuturesUnordered::new())),
5710 switch_item: None,
5711 high_priority: VecDeque::new(),
5712 low_priority: VecDeque::new(),
5713 suspend_reason: None,
5714 worker: None,
5715 worker_item: None,
5716 global_error_context_ref_counts: BTreeMap::new(),
5717 interesting_tasks: 0,
5718 interesting_tasks_empty_waker: None,
5719 ready_for_concurrent_call_waker: None,
5720 event_loop_running: false,
5721 }
5722 }
5723}
5724
5725impl ConcurrentState {
5726 pub(crate) fn take_fibers_and_futures(
5743 &mut self,
5744 fibers: &mut Vec<StoreFiber<'static>>,
5745 futures: &mut Vec<FuturesUnordered<HostTaskFuture>>,
5746 ) {
5747 let mut items = Vec::new();
5748 for entry in self.table.get_mut().iter_mut() {
5749 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5750 for mode in mem::take(&mut set.waiting).into_values() {
5751 match mode {
5752 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5753 fibers.push(fiber);
5754 }
5755 WaitMode::Callback(_) => {}
5756 }
5757 }
5758 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5759 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5760 mem::replace(&mut thread.state, GuestThreadState::Completed)
5761 {
5762 fibers.push(fiber);
5763 }
5764 } else if let Some(task) = entry.downcast_mut::<GuestTask>() {
5765 if let Some(item) = task.switch_item.take() {
5766 items.push(item);
5767 }
5768 }
5769 }
5770
5771 if let Some(fiber) = self.worker.take() {
5772 fibers.push(fiber);
5773 }
5774
5775 let mut handle_item = |item| match item {
5776 WorkItem::ResumeFiber { fiber, .. } => {
5777 fibers.push(fiber);
5778 }
5779 WorkItem::PushFuture(future) => {
5780 self.futures
5781 .get_mut()
5782 .as_mut()
5783 .unwrap()
5784 .push(future.into_inner());
5785 }
5786 WorkItem::ResumeThread { .. }
5787 | WorkItem::GuestCall { .. }
5788 | WorkItem::WorkerFunction(_) => {}
5789 };
5790
5791 for item in items {
5792 handle_item(item);
5793 }
5794 if let Some(item) = self.switch_item.take() {
5795 handle_item(item);
5796 }
5797 for item in mem::take(&mut self.high_priority) {
5798 handle_item(item);
5799 }
5800 for item in mem::take(&mut self.low_priority) {
5801 handle_item(item);
5802 }
5803
5804 if let Some(them) = self.futures.get_mut().take() {
5805 futures.push(them);
5806 }
5807 }
5808
5809 #[cfg(feature = "gc")]
5810 pub(crate) fn trace_fiber_roots(
5811 &mut self,
5812 modules: &ModuleRegistry,
5813 unwind: &dyn Unwind,
5814 gc_roots_list: &mut GcRootsList,
5815 ) {
5816 let ConcurrentState {
5817 table,
5818 worker,
5819 switch_item,
5820 high_priority,
5821 low_priority,
5822
5823 futures: _,
5827
5828 worker_item: _,
5830 unforced_current_thread: _,
5831 suspend_reason: _,
5832 global_error_context_ref_counts: _,
5833 interesting_tasks: _,
5834 interesting_tasks_empty_waker: _,
5835 ready_for_concurrent_call_waker: _,
5836 event_loop_running: _,
5837 } = self;
5838
5839 for entry in table.get_mut().iter_mut() {
5840 if let Some(set) = entry.downcast_mut::<WaitableSet>() {
5841 for mode in set.waiting.values_mut() {
5842 match mode {
5843 WaitMode::Fiber(fiber) | WaitMode::Caller { fiber, .. } => {
5844 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5845 }
5846 WaitMode::Callback(_) => {}
5847 }
5848 }
5849 } else if let Some(thread) = entry.downcast_mut::<GuestThread>() {
5850 if let GuestThreadState::Suspended(fiber) | GuestThreadState::Ready { fiber, .. } =
5851 &mut thread.state
5852 {
5853 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5854 }
5855 }
5856 }
5857
5858 if let Some(fiber) = worker {
5859 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5860 }
5861
5862 let mut handle_item = |item: &mut WorkItem| match item {
5863 WorkItem::ResumeFiber { fiber, .. } => {
5864 fiber.trace_gc_roots(modules, unwind, gc_roots_list);
5865 }
5866 WorkItem::PushFuture(_future) => {
5867 }
5870 WorkItem::ResumeThread { .. }
5871 | WorkItem::GuestCall { .. }
5872 | WorkItem::WorkerFunction(_) => {}
5873 };
5874
5875 if let Some(item) = switch_item {
5876 handle_item(item);
5877 }
5878 for item in high_priority {
5879 handle_item(item);
5880 }
5881 for item in low_priority {
5882 handle_item(item);
5883 }
5884 }
5885
5886 fn push<V: Send + Sync + 'static>(
5887 &mut self,
5888 value: V,
5889 ) -> Result<TableId<V>, ResourceTableError> {
5890 self.table.get_mut().push(value).map(TableId::from)
5891 }
5892
5893 fn get_mut<V: 'static>(&mut self, id: TableId<V>) -> Result<&mut V, ResourceTableError> {
5894 self.table.get_mut().get_mut(&Resource::from(id))
5895 }
5896
5897 pub fn add_child<T: 'static, U: 'static>(
5898 &mut self,
5899 child: TableId<T>,
5900 parent: TableId<U>,
5901 ) -> Result<(), ResourceTableError> {
5902 self.table
5903 .get_mut()
5904 .add_child(Resource::from(child), Resource::from(parent))
5905 }
5906
5907 pub fn remove_child<T: 'static, U: 'static>(
5908 &mut self,
5909 child: TableId<T>,
5910 parent: TableId<U>,
5911 ) -> Result<(), ResourceTableError> {
5912 self.table
5913 .get_mut()
5914 .remove_child(Resource::from(child), Resource::from(parent))
5915 }
5916
5917 fn delete<V: 'static>(&mut self, id: TableId<V>) -> Result<V, ResourceTableError> {
5918 self.table.get_mut().delete(Resource::from(id))
5919 }
5920
5921 fn push_future(&mut self, future: HostTaskFuture) {
5922 self.push_high_priority(WorkItem::PushFuture(AlwaysMut::new(future)));
5929 }
5930
5931 fn set_switch_item(&mut self, item: WorkItem) -> Result<()> {
5932 log::trace!("set switch item: {item:?}");
5933
5934 if self.switch_item.is_some() {
5935 bail_bug!("switch item already set");
5936 }
5937
5938 self.switch_item = Some(item);
5939
5940 Ok(())
5941 }
5942
5943 fn push_high_priority(&mut self, item: WorkItem) {
5944 log::trace!("push high priority: {item:?}");
5945 self.high_priority.push_front(item);
5946 }
5947
5948 fn push_low_priority(&mut self, item: WorkItem) {
5949 log::trace!("push low priority: {item:?}");
5950 self.low_priority.push_front(item);
5951 }
5952
5953 fn push_work_item(&mut self, item: WorkItem, priority: Priority) -> Result<()> {
5954 match priority {
5955 Priority::Switch => self.set_switch_item(item)?,
5956 Priority::High => self.push_high_priority(item),
5957 Priority::Low => self.push_low_priority(item),
5958 }
5959
5960 Ok(())
5961 }
5962
5963 fn promote_instance_local_thread_work_item(
5964 &mut self,
5965 current_instance: RuntimeInstance,
5966 ) -> Result<bool> {
5967 log::trace!("promote thread work items for {current_instance:?}");
5968
5969 self.promote_work_item_matching(|item: &WorkItem| {
5970 let result = match item {
5971 WorkItem::ResumeThread { instance, .. }
5972 | WorkItem::ResumeFiber { instance, .. }
5973 | WorkItem::GuestCall { instance, .. } => *instance == current_instance,
5974 _ => false,
5975 };
5976
5977 log::trace!("candidate {item:?}: {result}");
5978 result
5979 })
5980 }
5981
5982 fn promote_thread_work_item(&mut self, thread: QualifiedThreadId) -> Result<bool> {
5983 self.promote_work_item_matching(|item: &WorkItem| match item {
5984 WorkItem::ResumeThread {
5985 thread: item_thread,
5986 ..
5987 }
5988 | WorkItem::GuestCall {
5989 call:
5990 GuestCall {
5991 thread: item_thread,
5992 ..
5993 },
5994 ..
5995 } => *item_thread == thread,
5996 _ => false,
5997 })
5998 }
5999
6000 fn promote_work_item_matching<F>(&mut self, mut predicate: F) -> Result<bool>
6001 where
6002 F: FnMut(&WorkItem) -> bool,
6003 {
6004 for item in mem::take(&mut self.high_priority).into_iter().rev() {
6009 if self.switch_item.is_none() && predicate(&item) {
6010 self.set_switch_item(item)?;
6011 } else {
6012 self.push_high_priority(item);
6013 }
6014 }
6015
6016 if self.switch_item.is_none() {
6017 for item in mem::take(&mut self.low_priority).into_iter().rev() {
6018 if self.switch_item.is_none() && predicate(&item) {
6019 self.set_switch_item(item)?;
6020 } else {
6021 self.push_low_priority(item);
6022 }
6023 }
6024 }
6025
6026 Ok(self.switch_item.is_some())
6027 }
6028
6029 pub fn call_context(&mut self, task: u32) -> Result<&mut CallContext> {
6035 let (task, is_host) = (task >> 1, task & 1 == 1);
6036 if is_host {
6037 let task: TableId<HostTask> = TableId::new(task);
6038 Ok(&mut self.get_mut(task)?.call_context)
6039 } else {
6040 let task: TableId<GuestTask> = TableId::new(task);
6041 Ok(&mut self.get_mut(task)?.call_context)
6042 }
6043 }
6044
6045 fn futures_mut(&mut self) -> Result<&mut FuturesUnordered<HostTaskFuture>> {
6046 match self.futures.get_mut().as_mut() {
6047 Some(f) => Ok(f),
6048 None => bail_bug!("futures field of concurrent state is currently taken"),
6049 }
6050 }
6051
6052 pub(crate) fn table(&mut self) -> &mut ResourceTable {
6053 self.table.get_mut()
6054 }
6055
6056 fn parent(&mut self, cur: CurrentThread) -> Option<CurrentThread> {
6058 let task = match cur {
6059 CurrentThread::GuestTask(task) => task,
6060 CurrentThread::Guest(thread) => thread.task,
6061 CurrentThread::Host(id) => {
6062 return Some(CurrentThread::GuestTask(self.get_mut(id).ok()?.caller));
6063 }
6064 CurrentThread::None => return None,
6065 };
6066 let task = self.get_mut(task).ok()?;
6067 Some(match task.caller {
6068 Caller::Host { caller, .. } => caller,
6069 Caller::Guest { thread } => thread.into(),
6070 })
6071 }
6072}
6073
6074fn for_any_lower<
6077 F: FnOnce(&mut dyn VMStore, &mut [MaybeUninit<ValRaw>]) -> Result<()> + Send + Sync,
6078>(
6079 fun: F,
6080) -> F {
6081 fun
6082}
6083
6084fn for_any_lift<
6086 F: FnOnce(&mut dyn VMStore, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>> + Send + Sync,
6087>(
6088 fun: F,
6089) -> F {
6090 fun
6091}
6092
6093fn check_ambient_store(id: StoreId) {
6094 let message = "\
6095 `Future`s which depend on asynchronous component tasks, streams, or \
6096 futures to complete may only be polled from the event loop of the \
6097 store to which they belong. Please use \
6098 `StoreContextMut::{run_concurrent,spawn}` to poll or await them.\
6099 ";
6100 tls::try_get(|store| {
6101 let matched = match store {
6102 tls::TryGet::Some(store) => store.id() == id,
6103 tls::TryGet::Taken | tls::TryGet::None => false,
6104 };
6105
6106 if !matched {
6107 panic!("{message}")
6108 }
6109 });
6110}
6111
6112fn unpack_callback_code(code: u32) -> (u32, u32) {
6113 (code & 0xF, code >> 4)
6114}
6115
6116struct WaitableCheckParams {
6120 set: TableId<WaitableSet>,
6121 options: OptionsIndex,
6122 payload: u32,
6123}
6124
6125enum WaitableCheck {
6128 Wait,
6129 Poll,
6130}
6131
6132#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
6141pub struct GuestTaskId(TableId<GuestTask>);
6142
6143pub(crate) struct PreparedCall<R> {
6145 handle: Func,
6147 thread: QualifiedThreadId,
6149 param_count: usize,
6151 rx: oneshot::Receiver<LiftedResult>,
6154 runtime_instance: RuntimeInstance,
6156 _phantom: PhantomData<R>,
6157}
6158
6159impl<R> PreparedCall<R> {
6160 pub(crate) fn task_id(&self) -> TaskId {
6162 TaskId {
6163 task: self.thread.task,
6164 runtime_instance: self.runtime_instance,
6165 }
6166 }
6167}
6168
6169pub(crate) struct TaskId {
6171 task: TableId<GuestTask>,
6172 runtime_instance: RuntimeInstance,
6173}
6174
6175impl TaskId {
6176 pub(crate) fn host_future_dropped(&self, store: &mut StoreOpaque) -> Result<()> {
6182 let task = store.concurrent_state_mut()?.get_mut(self.task)?;
6183 let delete = if !task.already_lowered_parameters() {
6184 store.cancel_guest_subtask_without_lowered_parameters(
6185 self.runtime_instance,
6186 self.task,
6187 )?;
6188 true
6189 } else {
6190 task.host_future_state = HostFutureState::Dropped;
6191 task.ready_to_delete()
6192 };
6193 if delete {
6194 Waitable::Guest(self.task).delete_from(store.concurrent_state_mut()?)?
6195 }
6196 Ok(())
6197 }
6198}
6199
6200pub(crate) fn prepare_call<T, R>(
6206 mut store: StoreContextMut<T>,
6207 handle: Func,
6208 param_count: usize,
6209 host_future_present: bool,
6210 lower_params: impl FnOnce(StoreContextMut<T>, &mut [MaybeUninit<ValRaw>]) -> Result<()>
6211 + Send
6212 + Sync
6213 + 'static,
6214 lift_result: impl FnOnce(&mut StoreOpaque, &[ValRaw]) -> Result<Box<dyn Any + Send + Sync>>
6215 + Send
6216 + Sync
6217 + 'static,
6218) -> Result<PreparedCall<R>> {
6219 let (options, _flags, ty, raw_options) = handle.abi_info(store.0);
6220
6221 let instance = handle.instance().id().get(store.0);
6222 let options = &instance.component().env_component().options[options];
6223 let ty = &instance.component().types()[ty];
6224 let async_typed = ty.async_;
6225 let async_lifted = raw_options.async_;
6226 let task_return_type = ty.results;
6227 let component_instance = raw_options.instance;
6228 let callback = options.callback.map(|i| instance.runtime_callback(i));
6229 let memory = options
6230 .memory()
6231 .map(|i| instance.runtime_memory(i))
6232 .map(SendSyncPtr::new);
6233 let string_encoding = options.string_encoding;
6234 let token = StoreToken::new(store.as_context_mut());
6235 let caller = store.0.current_thread()?;
6236 let state = store.0.concurrent_state_mut()?;
6237
6238 let (tx, rx) = oneshot::channel();
6239
6240 let instance = handle.instance().runtime_instance(component_instance);
6241 let thread = GuestTask::new(
6242 state,
6243 Box::new(for_any_lower(move |store, params| {
6244 lower_params(token.as_context_mut(store), params)
6245 })),
6246 LiftResult {
6247 lift: Box::new(for_any_lift(move |store, result| {
6248 lift_result(store, result)
6249 })),
6250 ty: task_return_type,
6251 memory,
6252 string_encoding,
6253 },
6254 Caller::Host {
6255 tx: Some(tx),
6256 host_future_present,
6257 caller,
6258 },
6259 callback.map(|callback| {
6260 let callback = SendSyncPtr::new(callback);
6261 let instance = handle.instance();
6262 Box::new(move |store: &mut dyn VMStore, event, handle| {
6263 let store = token.as_context_mut(store);
6264 unsafe { instance.call_callback(store, callback, event, handle) }
6267 }) as CallbackFn
6268 }),
6269 instance,
6270 async_typed,
6271 async_lifted,
6272 )?;
6273
6274 if !store.0.may_enter() {
6275 bail!(Trap::CannotEnterComponent);
6276 }
6277
6278 Ok(PreparedCall {
6279 handle,
6280 thread,
6281 param_count,
6282 runtime_instance: instance,
6283 rx,
6284 _phantom: PhantomData,
6285 })
6286}
6287
6288pub(crate) struct StagedCall<R> {
6289 store: StoreId,
6290 task: TableId<GuestTask>,
6291 rx: oneshot::Receiver<LiftedResult>,
6292 _marker: PhantomData<fn() -> R>,
6293}
6294
6295impl<R> StagedCall<R> {
6296 pub(crate) fn new<T: 'static>(
6303 mut store: StoreContextMut<T>,
6304 prepared: PreparedCall<R>,
6305 ) -> Result<StagedCall<R>> {
6306 let PreparedCall {
6307 handle,
6308 thread,
6309 param_count,
6310 rx,
6311 ..
6312 } = prepared;
6313
6314 stage_call0(store.as_context_mut(), handle, thread, param_count)?;
6315
6316 Ok(StagedCall {
6317 store: store.0.id(),
6318 task: thread.task,
6319 rx,
6320 _marker: PhantomData,
6321 })
6322 }
6323
6324 fn task(&self) -> GuestTaskId {
6325 GuestTaskId(self.task)
6326 }
6327}
6328
6329impl<R> Future for StagedCall<R>
6330where
6331 R: 'static,
6332{
6333 type Output = Result<R>;
6334
6335 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
6336 check_ambient_store(self.store);
6337 Pin::new(&mut self.rx).poll(cx).map(|result| match result {
6338 Ok(r) => match r.downcast() {
6339 Ok(r) => Ok(*r),
6340 Err(_) => bail_bug!("wrong type of value produced"),
6341 },
6342 Err(oneshot::Canceled) => bail_bug!("channel erroneously dropped"),
6343 })
6344 }
6345}
6346
6347fn stage_call0<T: 'static>(
6350 store: StoreContextMut<T>,
6351 handle: Func,
6352 guest_thread: QualifiedThreadId,
6353 param_count: usize,
6354) -> Result<()> {
6355 let (_options, _, _ty, raw_options) = handle.abi_info(store.0);
6356 let is_concurrent = raw_options.async_;
6357 let callback = raw_options.callback;
6358 let instance = handle.instance();
6359 let callee = handle.lifted_core_func(store.0);
6360 let post_return = raw_options
6361 .post_return
6362 .map(|i| instance.id().get(store.0).runtime_post_return(i));
6363 let callback = callback.map(|i| {
6364 let instance = instance.id().get(store.0);
6365 SendSyncPtr::new(instance.runtime_callback(i))
6366 });
6367
6368 log::trace!("queueing call {guest_thread:?}");
6369
6370 unsafe {
6374 instance.stage_call(
6375 store,
6376 guest_thread,
6377 SendSyncPtr::new(callee),
6378 param_count,
6379 1,
6380 is_concurrent,
6381 callback,
6382 post_return.map(SendSyncPtr::new),
6383 true,
6384 )
6385 }
6386}