wasmtime/runtime/vm/traphandlers.rs
1//! WebAssembly trap handling, which is built on top of the lower-level
2//! signalhandling mechanisms.
3
4mod backtrace;
5
6#[cfg(feature = "coredump")]
7#[path = "traphandlers/coredump_enabled.rs"]
8mod coredump;
9#[cfg(not(feature = "coredump"))]
10#[path = "traphandlers/coredump_disabled.rs"]
11mod coredump;
12
13#[cfg(all(has_native_signals))]
14mod signals;
15#[cfg(all(has_native_signals))]
16pub use self::signals::*;
17
18#[cfg(feature = "gc")]
19use crate::ThrownException;
20use crate::runtime::module::lookup_code;
21use crate::runtime::store::{ExecutorRef, StoreOpaque};
22use crate::runtime::vm::sys::traphandlers;
23use crate::runtime::vm::{InterpreterRef, VMContext, VMStore, VMStoreContext, f32x4, f64x2, i8x16};
24use crate::{EntryStoreContext, prelude::*};
25use crate::{StoreContextMut, WasmBacktrace};
26use core::cell::Cell;
27use core::num::NonZeroU32;
28use core::ptr::{self, NonNull};
29use wasmtime_unwinder::Handler;
30
31#[cfg(feature = "debug")]
32pub(crate) use self::backtrace::Activation;
33pub use self::backtrace::Backtrace;
34#[cfg(feature = "gc")]
35pub use wasmtime_unwinder::Frame;
36
37pub use self::coredump::CoreDumpStack;
38pub use self::tls::tls_eager_initialize;
39#[cfg(feature = "async")]
40pub use self::tls::{AsyncWasmCallState, PreviousAsyncWasmCallState};
41
42pub use traphandlers::SignalHandler;
43
44pub(crate) struct TrapRegisters {
45 pub pc: usize,
46 pub fp: usize,
47}
48
49/// Return value from `test_if_trap`.
50pub(crate) enum TrapTest {
51 /// Not a wasm trap, need to delegate to whatever process handler is next.
52 NotWasm,
53 /// This trap was handled by the embedder via custom embedding APIs.
54 #[cfg(has_host_compiler_backend)]
55 #[cfg_attr(miri, expect(dead_code, reason = "using #[cfg] too unergonomic"))]
56 HandledByEmbedder,
57 /// This is a wasm trap, it needs to be handled.
58 Trap(Handler),
59}
60
61fn lazy_per_thread_init() {
62 traphandlers::lazy_per_thread_init();
63}
64
65/// Raises a preexisting trap or exception and unwinds.
66///
67/// If the preexisting state has registered a trap, this function will execute
68/// the `Handler::resume` to make its way back to the original exception
69/// handler created when Wasm was entered. If the state has registered an
70/// exception, this function will perform the unwind action registered: either
71/// resetting PC, FP, and SP to the handler in the middle of the Wasm
72/// activation on the stack, or the entry trampoline back to the the host, if
73/// the exception is uncaught.
74///
75/// This is currently only called from the `raise` builtin of
76/// Wasmtime. This builtin is only used when the host returns back to
77/// wasm and indicates that a trap or exception should be raised. In
78/// this situation the host has already stored trap or exception
79/// information within the `CallThreadState` and this is the low-level
80/// operation to actually perform an unwind.
81///
82/// Note that this function is used both for Pulley and for native execution.
83/// For Pulley this function will return and the interpreter will be
84/// responsible for handling the control-flow transfer. For native this
85/// function will not return as the control flow transfer will be handled
86/// internally.
87///
88/// # Safety
89///
90/// Only safe to call when wasm code is on the stack, aka `catch_traps` must
91/// have been previously called. Additionally no Rust destructors can be on the
92/// stack. They will be skipped and not executed.
93pub(super) unsafe fn raise_preexisting_trap(store: &mut dyn VMStore) {
94 tls::with(|info| unsafe { info.unwrap().unwind(store) })
95}
96
97/// Invokes the closure `f` and handles any error/panic/trap that happens
98/// within.
99///
100/// This will invoke the closure `f` with the provided `store` and the closure
101/// will return a value that implements `HostResult`. This trait abstracts over
102/// how host values are translated to ABI values when going back into wasm.
103/// Some examples are:
104///
105/// * `T` - bare return types (not results) are simply returned as-is. No
106/// `catch_unwind` happens as if a trap can't happen then the host shouldn't
107/// be panicking or invoking user code.
108///
109/// * `Result<(), E>` - this represents an ABI return value of `bool` which
110/// indicates whether the call succeeded. This return value will catch panics
111/// and record trap information as `E`.
112///
113/// * `Result<u32, E>` - the ABI return value here is `u64` where on success
114/// the 32-bit result is zero-extended and `u64::MAX` as a return value
115/// indicates that a trap or panic happened.
116///
117/// This is primarily used in conjunction with the Cranelift-and-host boundary.
118/// This function acts as a bridge between the two to appropriately handle
119/// encoding host values to Cranelift-understood ABIs via the `HostResult`
120/// trait.
121pub fn catch_unwind_and_record_trap<R>(
122 store: &mut dyn VMStore,
123 f: impl FnOnce(&mut dyn VMStore) -> R,
124) -> R::Abi
125where
126 R: HostResult,
127{
128 // Invoke the closure `f`, optionally catching unwinds depending on `R`. The
129 // return value is always provided and if unwind information is provided
130 // (e.g. `ret` is a "false"-y value) then it's recorded in TLS for the
131 // unwind operation that's about to happen from Cranelift-generated code.
132 let (ret, unwind) = R::maybe_catch_unwind(store, |store| f(store));
133 if let Some(unwind) = unwind {
134 tls::with(|info| info.unwrap().record_unwind(unwind));
135 }
136 ret
137}
138
139/// A trait used in conjunction with `catch_unwind_and_record_trap` to convert a
140/// Rust-based type to a specific ABI while handling traps/unwinds.
141///
142/// This type is implemented for return values from host function calls and
143/// libcalls. The `Abi` value of this trait represents either a successful
144/// execution with some payload state or that a failed execution happened. In
145/// the event of a failed execution the state of the failure itself is stored
146/// within `CallThreadState::unwind`. Cranelift-compiled code is expected to
147/// test for this failure sentinel and process it accordingly.
148///
149/// See `catch_unwind_and_record_trap` for some more information as well.
150pub trait HostResult {
151 /// The type of the value that's returned to Cranelift-compiled code. Needs
152 /// to be ABI-safe to pass through an `extern "C"` return value.
153 type Abi: Copy;
154
155 /// Executes `f` and returns the ABI/unwind information as a result.
156 ///
157 /// This may optionally catch unwinds during execution depending on this
158 /// implementation. The ABI return value is unconditionally provided. If an
159 /// unwind was detected (e.g. a host panic or a wasm trap) then that's
160 /// additionally returned as well.
161 ///
162 /// If an unwind is returned then it's expected that when the host returns
163 /// back to wasm (which should be soon after calling this through
164 /// `catch_unwind_and_record_trap`) then wasm will very quickly turn around
165 /// and initiate an unwind (currently through `raise_preexisting_trap`).
166 fn maybe_catch_unwind(
167 store: &mut dyn VMStore,
168 f: impl FnOnce(&mut dyn VMStore) -> Self,
169 ) -> (Self::Abi, Option<UnwindReason>);
170}
171
172// Base case implementations that do not catch unwinds. These are for libcalls
173// that neither trap nor execute user code. The raw value is the ABI itself.
174//
175// Panics in these libcalls will result in a process abort as unwinding is not
176// allowed via Rust through `extern "C"` function boundaries.
177macro_rules! host_result_no_catch {
178 ($($t:ty,)*) => {
179 $(
180 impl HostResult for $t {
181 type Abi = $t;
182 #[allow(unreachable_code, reason = "some types uninhabited on some platforms")]
183 fn maybe_catch_unwind(
184 store: &mut dyn VMStore,
185 f: impl FnOnce(&mut dyn VMStore) -> $t,
186 ) -> ($t, Option<UnwindReason>) {
187 (f(store), None)
188 }
189 }
190 )*
191 }
192}
193
194host_result_no_catch! {
195 (),
196 bool,
197 u32,
198 *mut u8,
199 u64,
200 f32,
201 f64,
202 usize,
203 i8x16,
204 f32x4,
205 f64x2,
206}
207
208impl HostResult for NonNull<u8> {
209 type Abi = *mut u8;
210 fn maybe_catch_unwind(
211 store: &mut dyn VMStore,
212 f: impl FnOnce(&mut dyn VMStore) -> Self,
213 ) -> (*mut u8, Option<UnwindReason>) {
214 (f(store).as_ptr(), None)
215 }
216}
217
218/// Implementation of `HostResult` for `Result<T, E>`.
219///
220/// This is where things get interesting for `HostResult`. This is generically
221/// defined to allow many shapes of the `Result` type to be returned from host
222/// calls or libcalls. To do this an extra trait requirement is placed on the
223/// successful result `T`: `HostResultHasUnwindSentinel`.
224///
225/// The general requirement is that `T` says what ABI it has, and the ABI must
226/// have a sentinel value which indicates that an unwind in wasm should happen.
227/// For example if `T = ()` then `true` means that the call succeeded and
228/// `false` means that an unwind happened. Here the sentinel is `false` and the
229/// ABI is `bool`.
230///
231/// This is the only implementation of `HostResult` which actually catches
232/// unwinds as there's a sentinel to encode.
233impl<T, E> HostResult for Result<T, E>
234where
235 T: HostResultHasUnwindSentinel,
236 E: Into<TrapReason>,
237{
238 type Abi = T::Abi;
239
240 fn maybe_catch_unwind(
241 store: &mut dyn VMStore,
242 f: impl FnOnce(&mut dyn VMStore) -> Result<T, E>,
243 ) -> (T::Abi, Option<UnwindReason>) {
244 // First prepare the closure `f` as something that'll be invoked to
245 // generate the return value of this function. This is the
246 // conditionally, below, passed to `catch_unwind`.
247 let f = move || match f(store) {
248 Ok(ret) => {
249 let abi = ret.into_abi();
250 debug_assert!(abi != T::SENTINEL);
251 (abi, None)
252 }
253 Err(reason) => (T::SENTINEL, Some(UnwindReason::from(reason))),
254 };
255
256 // With `panic=unwind` use `std::panic::catch_unwind` to catch possible
257 // panics to rethrow.
258 #[cfg(all(feature = "std", panic = "unwind"))]
259 {
260 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
261 Ok(result) => result,
262 Err(err) => (T::SENTINEL, Some(UnwindReason::Panic(err))),
263 }
264 }
265
266 // With `panic=abort` there's no use in using `std::panic::catch_unwind`
267 // since it won't actually catch anything. Note that
268 // `std::panic::catch_unwind` will technically optimize to this but having
269 // this branch avoids using the `std::panic` module entirely.
270 #[cfg(not(all(feature = "std", panic = "unwind")))]
271 {
272 f()
273 }
274 }
275}
276
277/// Trait used in conjunction with `HostResult for Result<T, E>` where this is
278/// the trait bound on `T`.
279///
280/// This is for values in the "ok" position of a `Result` return value. Each
281/// value can have a separate ABI from itself (e.g. `type Abi`) and must be
282/// convertible to the ABI. Additionally all implementations of this trait have
283/// a "sentinel value" which indicates that an unwind happened. This means that
284/// no valid instance of `Self` should generate the `SENTINEL` via the
285/// `into_abi` function.
286pub unsafe trait HostResultHasUnwindSentinel {
287 /// The Cranelift-understood ABI of this value (should not be `Self`).
288 type Abi: Copy + PartialEq;
289
290 /// A value that indicates that an unwind should happen and is tested for in
291 /// Cranelift-generated code.
292 const SENTINEL: Self::Abi;
293
294 /// Converts this value into the ABI representation. Should never returned
295 /// the `SENTINEL` value.
296 fn into_abi(self) -> Self::Abi;
297}
298
299/// No return value from the host is represented as a `bool` in the ABI. Here
300/// `true` means that execution succeeded while `false` is the sentinel used to
301/// indicate an unwind.
302unsafe impl HostResultHasUnwindSentinel for () {
303 type Abi = bool;
304 const SENTINEL: bool = false;
305 fn into_abi(self) -> bool {
306 true
307 }
308}
309
310unsafe impl HostResultHasUnwindSentinel for NonZeroU32 {
311 type Abi = u32;
312 const SENTINEL: Self::Abi = 0;
313 fn into_abi(self) -> Self::Abi {
314 self.get()
315 }
316}
317
318/// A 32-bit return value can be inflated to a 64-bit return value in the ABI.
319/// In this manner a successful result is a zero-extended 32-bit value and the
320/// failure sentinel is `u64::MAX` or -1 as a signed integer.
321unsafe impl HostResultHasUnwindSentinel for u32 {
322 type Abi = u64;
323 const SENTINEL: u64 = u64::MAX;
324 fn into_abi(self) -> u64 {
325 self.into()
326 }
327}
328
329/// If there is not actual successful result (e.g. an empty enum) then the ABI
330/// can be `()`, or nothing, because there's no successful result and it's
331/// always a failure.
332unsafe impl HostResultHasUnwindSentinel for core::convert::Infallible {
333 type Abi = ();
334 const SENTINEL: () = ();
335 fn into_abi(self) {
336 match self {}
337 }
338}
339
340unsafe impl HostResultHasUnwindSentinel for bool {
341 type Abi = u32;
342 const SENTINEL: Self::Abi = u32::MAX;
343 fn into_abi(self) -> Self::Abi {
344 u32::from(self)
345 }
346}
347
348unsafe impl HostResultHasUnwindSentinel for *mut u8 {
349 type Abi = *mut u8;
350 const SENTINEL: Self::Abi = ptr::without_provenance_mut(usize::MAX);
351 fn into_abi(self) -> Self::Abi {
352 self
353 }
354}
355
356/// A helper structure to schlep from this module to the
357/// `crate::trap::from_runtime_box` function.
358///
359/// This is boxed up on the heap to keep movement around optimized. This is
360/// mutated after creation to fill in `backtrace` in some situations as well.
361#[derive(Debug)]
362pub struct Trap {
363 /// Original reason from where this trap originated.
364 pub reason: TrapReason,
365 /// Wasm backtrace of the trap, if any.
366 pub backtrace: Option<Backtrace>,
367 /// The Wasm Coredump, if any.
368 pub coredumpstack: Option<CoreDumpStack>,
369}
370
371/// Enumeration of different methods of raising a trap (or a sentinel
372/// for an exception).
373#[derive(Debug)]
374pub enum TrapReason {
375 /// A user-defined error has been raised, such as through a host function
376 /// call.
377 ///
378 /// This is constructed naturally through various `From` conversions leading
379 /// into a `TrapReason`. For example host functions returning `Result<()>`
380 /// will have any errors put here.
381 ///
382 /// Note that this variant can also represent an embedder-thrown exception.
383 /// Embedder-thrown exceptions are encoded as `ThrownException.into()` which
384 /// then looks for various handlers on the stack.
385 User(Error),
386
387 /// A trap raised from Cranelift-generated code.
388 Jit {
389 /// The program counter where this trap originated.
390 ///
391 /// This is later used with side tables from compilation to translate
392 /// the trapping address to a trap code.
393 pc: usize,
394
395 /// If the trap was a memory-related trap such as SIGSEGV then this
396 /// field will contain the address of the inaccessible data.
397 ///
398 /// Note that wasm loads/stores are not guaranteed to fill in this
399 /// information. Dynamically-bounds-checked memories, for example, will
400 /// not access an invalid address but may instead load from NULL or may
401 /// explicitly jump to a `ud2` instruction. This is only available for
402 /// fault-based traps which are one of the main ways, but not the only
403 /// way, to run wasm.
404 faulting_addr: Option<usize>,
405
406 /// The trap code associated with this trap.
407 trap: wasmtime_environ::CompiledTrap,
408 },
409}
410
411impl<E> From<E> for TrapReason
412where
413 E: Into<Error>,
414{
415 fn from(error: E) -> Self {
416 TrapReason::User(error.into())
417 }
418}
419
420/// Catches any wasm traps that happen within the execution of `closure`,
421/// returning them as a `Result`.
422pub fn catch_traps<T, F>(
423 store: &mut StoreContextMut<'_, T>,
424 old_state: &mut EntryStoreContext,
425 mut closure: F,
426) -> Result<()>
427where
428 F: FnMut(NonNull<VMContext>, Option<InterpreterRef<'_>>) -> bool,
429{
430 let caller = store.0.default_caller();
431
432 let result = CallThreadState::new(store.0, old_state).with(|_cx| match store.0.executor() {
433 ExecutorRef::Interpreter(r) => closure(caller, Some(r)),
434 #[cfg(has_host_compiler_backend)]
435 ExecutorRef::Native => closure(caller, None),
436 });
437
438 match result {
439 Ok(x) => Ok(x),
440 Err(UnwindReason::Trap(reason)) => Err(crate::trap::from_runtime_box(store.0, reason?)),
441 #[cfg(all(feature = "std", panic = "unwind"))]
442 Err(UnwindReason::Panic(panic)) => std::panic::resume_unwind(panic),
443 }
444}
445
446// Module to hide visibility of the `CallThreadState::prev` field and force
447// usage of its accessor methods.
448mod call_thread_state {
449 use super::*;
450 use crate::EntryStoreContext;
451 use crate::runtime::vm::{Unwind, VMStackChain};
452
453 /// Temporary state stored on the stack which is registered in the `tls`
454 /// module below for calls into wasm.
455 ///
456 /// This structure is stored on the stack and allocated during the
457 /// `catch_traps` function above. The purpose of this structure is to track
458 /// the state of an "activation" or a sequence of 0-or-more contiguous
459 /// WebAssembly call frames. A `CallThreadState` always lives on the stack
460 /// and additionally maintains pointers to previous states to form a linked
461 /// list of activations.
462 ///
463 /// One of the primary goals of `CallThreadState` is to store the state of
464 /// various fields in `VMStoreContext` when it was created. This is done
465 /// because calling WebAssembly will clobber these fields otherwise.
466 ///
467 /// Another major purpose of `CallThreadState` is to assist with unwinding
468 /// and track state necessary when an unwind happens for the original
469 /// creator of `CallThreadState` to determine why the unwind happened.
470 ///
471 /// Note that this structure is pointed-to from TLS, hence liberal usage of
472 /// interior mutability here since that only gives access to
473 /// `&CallThreadState`.
474 pub struct CallThreadState {
475 /// Unwind state set when initiating an unwind and read when
476 /// the control transfer occurs (after the `raise` point is
477 /// reached for host-code destinations and right when
478 /// performing the jump for Wasm-code destinations).
479 pub(super) unwind: Cell<Option<UnwindReason>>,
480 #[cfg(all(has_native_signals))]
481 pub(super) signal_handler: Option<*const SignalHandler>,
482 pub(super) capture_backtrace: bool,
483 #[cfg(feature = "coredump")]
484 pub(super) capture_coredump: bool,
485
486 pub(crate) vm_store_context: Cell<NonNull<VMStoreContext>>,
487 pub(crate) unwinder: &'static dyn Unwind,
488
489 pub(super) prev: Cell<tls::Ptr>,
490
491 // The state of the runtime for the *previous* `CallThreadState` for
492 // this same store. Our *current* state is saved in `self.vm_store_context`,
493 // etc. We need access to the old values of these
494 // fields because the `VMStoreContext` typically doesn't change across
495 // nested calls into Wasm (i.e. they are typically calls back into the
496 // same store and `self.vm_store_context == self.prev.vm_store_context`) and we must to
497 // maintain the list of contiguous-Wasm-frames stack regions for
498 // backtracing purposes.
499 old_state: *mut EntryStoreContext,
500 }
501
502 impl Drop for CallThreadState {
503 fn drop(&mut self) {
504 // Unwind information should not be present as it should have
505 // already been processed.
506 debug_assert!(self.unwind.replace(None).is_none());
507 }
508 }
509
510 impl CallThreadState {
511 #[inline]
512 pub(super) fn new(
513 store: &mut StoreOpaque,
514 old_state: *mut EntryStoreContext,
515 ) -> CallThreadState {
516 CallThreadState {
517 unwind: Cell::new(None),
518 unwinder: store.unwinder(),
519 #[cfg(all(has_native_signals))]
520 signal_handler: store.signal_handler(),
521 capture_backtrace: store.engine().config().wasm_backtrace_max_frames.is_some(),
522 #[cfg(feature = "coredump")]
523 capture_coredump: store.engine().config().coredump_on_trap,
524 vm_store_context: Cell::new(store.vm_store_context_ptr()),
525 prev: Cell::new(ptr::null()),
526 old_state,
527 }
528 }
529
530 /// Get the saved FP upon exit from Wasm for the previous `CallThreadState`.
531 ///
532 /// # Safety
533 ///
534 /// Requires that the saved last Wasm trampoline FP points to
535 /// a valid trampoline frame, or is null.
536 pub unsafe fn old_last_wasm_exit_fp(&self) -> usize {
537 let trampoline_fp = unsafe { (&*self.old_state).last_wasm_exit_trampoline_fp };
538 // SAFETY: `trampoline_fp` is either a valid FP from an
539 // active trampoline frame or is null.
540 unsafe { VMStoreContext::wasm_exit_fp_from_trampoline_fp(trampoline_fp) }
541 }
542
543 /// Get the saved PC upon exit from Wasm for the previous `CallThreadState`.
544 pub unsafe fn old_last_wasm_exit_pc(&self) -> usize {
545 unsafe { (&*self.old_state).last_wasm_exit_pc }
546 }
547
548 /// Get the saved FP upon entry into Wasm for the previous `CallThreadState`.
549 pub unsafe fn old_last_wasm_entry_fp(&self) -> usize {
550 unsafe { (&*self.old_state).last_wasm_entry_fp }
551 }
552
553 /// Get the saved `VMStackChain` for the previous `CallThreadState`.
554 pub unsafe fn old_stack_chain(&self) -> VMStackChain {
555 unsafe { (&*self.old_state).stack_chain.clone() }
556 }
557
558 /// Get the previous `CallThreadState`.
559 pub fn prev(&self) -> tls::Ptr {
560 self.prev.get()
561 }
562
563 /// Pushes this `CallThreadState` activation on to the linked list
564 /// stored in TLS.
565 ///
566 /// This method will take the current head of the linked list, stored in
567 /// our TLS pointer, and move it into `prev`. The TLS pointer is then
568 /// updated to `self`.
569 ///
570 /// # Panics
571 ///
572 /// Panics if this activation is already in a linked list (e.g.
573 /// `self.prev` is set).
574 #[inline]
575 pub(crate) unsafe fn push(&self) {
576 assert!(self.prev.get().is_null());
577 self.prev.set(tls::raw::replace(self));
578 }
579
580 /// Pops this `CallThreadState` from the linked list stored in TLS.
581 ///
582 /// This method will restore `self.prev` into the head of the linked
583 /// list stored in TLS and will additionally null-out `self.prev`.
584 ///
585 /// # Panics
586 ///
587 /// Panics if this activation isn't the head of the list.
588 #[inline]
589 pub(crate) unsafe fn pop(&self) {
590 let prev = self.prev.replace(ptr::null());
591 let head = tls::raw::replace(prev);
592 assert!(core::ptr::eq(head, self));
593 }
594
595 /// Swaps the state in this `CallThreadState`'s `VMStoreContext` with
596 /// the state in `EntryStoreContext` that was saved when this
597 /// activation was created.
598 ///
599 /// This method is using during suspension of a fiber to restore the
600 /// store back to what it originally was and prepare it to be resumed
601 /// later on. This takes various fields of `VMStoreContext` and swaps
602 /// them with what was saved in `EntryStoreContext`. That restores
603 /// a store to just before this activation was called but saves off the
604 /// fields of this activation to get restored/resumed at a later time.
605 #[cfg(feature = "async")]
606 pub(super) unsafe fn swap(&self) {
607 unsafe fn swap<T>(a: &core::cell::UnsafeCell<T>, b: &mut T) {
608 unsafe { core::mem::swap(&mut *a.get(), b) }
609 }
610
611 unsafe {
612 let cx = self.vm_store_context.get().as_ref();
613 swap(
614 &cx.last_wasm_exit_trampoline_fp,
615 &mut (*self.old_state).last_wasm_exit_trampoline_fp,
616 );
617 swap(
618 &cx.last_wasm_exit_pc,
619 &mut (*self.old_state).last_wasm_exit_pc,
620 );
621 swap(
622 &cx.last_wasm_entry_fp,
623 &mut (*self.old_state).last_wasm_entry_fp,
624 );
625 swap(
626 &cx.last_wasm_entry_sp,
627 &mut (*self.old_state).last_wasm_entry_sp,
628 );
629 swap(
630 &cx.last_wasm_entry_trap_handler,
631 &mut (*self.old_state).last_wasm_entry_trap_handler,
632 );
633 swap(&cx.stack_chain, &mut (*self.old_state).stack_chain);
634 }
635 }
636 }
637}
638pub use call_thread_state::*;
639
640#[cfg(feature = "gc")]
641use super::compute_handler;
642
643/// The reasons why Wasmtime might unwind, stored within `CallThreadState`.
644pub enum UnwindReason {
645 /// The host panicked.
646 ///
647 /// In this situation Wasmtime must transfer the panic payload across Wasm
648 /// code since the native unwinder isn't guaranteed to be able to unwind
649 /// wasm. Once wasm is unwound, however, the panic is re-thrown on the
650 /// other side to propagate like usual.
651 #[cfg(all(feature = "std", panic = "unwind"))]
652 Panic(Box<dyn std::any::Any + Send>),
653
654 /// Wasm or the host raised a trap for some reason.
655 ///
656 /// This is specifically stored as a `Result` to carry the `OutOfMemory`
657 /// error from when this is allocated to the catch-site of the error.
658 /// Otherwise keeping this in a `Box` means that moving this value in and
659 /// out of `CallThreadState` is optimized. Specifically the "hot function"
660 /// doesn't need a slow path which is much larger with lots of memcpy's and
661 /// such.
662 Trap(Result<Box<Trap>, OutOfMemory>),
663}
664
665impl<E> From<E> for UnwindReason
666where
667 E: Into<TrapReason>,
668{
669 fn from(value: E) -> UnwindReason {
670 UnwindReason::Trap(try_new::<Box<_>>(Trap {
671 reason: value.into(),
672 backtrace: None,
673 coredumpstack: None,
674 }))
675 }
676}
677
678impl CallThreadState {
679 #[inline]
680 fn with(mut self, closure: impl FnOnce(&CallThreadState) -> bool) -> Result<(), UnwindReason> {
681 let succeeded = tls::set(&mut self, |me| closure(me));
682 if succeeded {
683 Ok(())
684 } else {
685 Err(self.read_unwind())
686 }
687 }
688
689 #[cold]
690 fn read_unwind(&self) -> UnwindReason {
691 self.unwind.replace(None).unwrap()
692 }
693
694 /// Records the unwind information provided within this `CallThreadState`.
695 ///
696 /// This function is used to stash metadata for why an unwind is about to
697 /// happen. The actual unwind is expected to happen after this function is
698 /// called using the `unwind` function below. This function is expected to
699 /// be called from the host or a signal handler the moment a trap happens.
700 /// Signal handlers then immediately unwind to the entry state, and host
701 /// functions will return back to the entry trampoline which will
702 /// immediately turn around and call the `unwind` function below.
703 ///
704 /// Note that this is a relatively low-level function and will panic if
705 /// misused.
706 ///
707 /// # Panics
708 ///
709 /// Panics if unwind information has already been recorded as that should
710 /// have been processed first.
711 fn record_unwind(&self, reason: UnwindReason) {
712 if cfg!(debug_assertions) {
713 let prev = self.unwind.replace(None);
714 assert!(prev.is_none());
715 }
716
717 self.unwind.set(Some(reason));
718 }
719
720 /// Helper function to perform an actual unwinding operation.
721 ///
722 /// This must be preceded by a `record_unwind` operation above to be
723 /// processed correctly on the other side.
724 ///
725 /// This is not used for signals-based-traps. When a signal is caught the
726 /// thread's register state is updated to the entrypoint handler. This is
727 /// only used for host-initiated traps. Note that this includes the host
728 /// implementation of throwing a wasm exception.
729 ///
730 /// Note that this function is expected to be called with the wasm backtrace
731 /// in such a state that it represents the unwinding condition.
732 /// Effectively, if a wasm backtrace is captured here, it reflects why the
733 /// unwind happened.
734 ///
735 /// # Unsafety
736 ///
737 /// This function is not safe if a corresponding handler wasn't already
738 /// setup in the entry trampoline. Additionally this isn't safe as it may
739 /// skip all Rust destructors on the stack, if there are any, for native
740 /// executors as `Handler::resume` will be used.
741 unsafe fn unwind(&self, store: &mut dyn VMStore) {
742 #[allow(unused_mut, reason = "only mutated in `debug` configuration")]
743 let mut unwind = self.unwind.replace(None);
744
745 // If configured, fire a debug event for the cause of unwinding here.
746 //
747 // Note that by firing a debug event the trap being handled can be
748 // subtly different. For example in the event of a thrown exception the
749 // debug handler might take the exception from the store and put
750 // another one there, changing the type of the exception being thrown.
751 // This notably means that the calculation for what to do about `unwind`
752 // happens after this block, down below.
753 //
754 // Also note that this can execute arbitrary WebAssembly code within
755 // this block due to the store's debug handler. That means that we
756 // might be paused here for quite some time.
757 #[cfg(feature = "debug")]
758 if let Some(UnwindReason::Trap(Ok(trap))) = &unwind {
759 #[cfg(feature = "gc")]
760 use wasmtime_core::alloc::PanicOnOom;
761
762 let result = match &trap.reason {
763 TrapReason::User(err) => {
764 let mut event = crate::DebugEvent::HostcallError(err);
765 if let Some(trap) = err.downcast_ref() {
766 event = crate::DebugEvent::Trap(*trap);
767 }
768
769 if let Some(trap) = err.downcast_ref() {
770 event = crate::DebugEvent::Trap(*trap);
771 }
772
773 // For `ThrownException` errors that indicates that we should
774 // look at the store to see if there's a pending exception
775 // there, and if so then that's a different debug event than the
776 // `HostcallError`.
777 //
778 // TODO(#12069): handle allocation failure here
779 #[cfg(feature = "gc")]
780 if err.is::<ThrownException>()
781 && let Some(exn) = store.pending_exception_owned_rooted().panic_on_oom()
782 {
783 event = crate::DebugEvent::Exception(exn.clone());
784 }
785
786 store.block_on_debug_handler(event)
787 }
788
789 TrapReason::Jit { .. } => {
790 // Not handled here. JIT traps only show up via signal
791 // handlers, and the debugger isn't invoked from signal
792 // handlers at this time.
793 Ok(())
794 }
795 };
796
797 // If the debugger invocation itself resulted in an `Err`
798 // (which can only come from the `block_on` hitting a
799 // failure mode), we need to override our unwind as-if
800 // were handling a host error.
801 if let Err(err) = result {
802 unwind = Some(UnwindReason::from(err));
803 }
804 }
805
806 let handler;
807 let payload1;
808 let payload2;
809
810 // Determine, from `unwind`, the `handler` and payloads that will be
811 // used to resume execution to. Note that the entry trampoline into wasm
812 // setup an entrypoint handler meaning we're guaranteed *something* to
813 // unwind to. In the case of a wasm exception, however, we may want to
814 // unwind to a different landing pad on the stack which is in-wasm.
815 'done: {
816 if let Some(UnwindReason::Trap(Ok(trap))) = &mut unwind {
817 let mut has_backtrace = trap.backtrace.is_some();
818
819 if let TrapReason::User(err) = &trap.reason {
820 // If this trap indicates an exception is being thrown, aka
821 // `ThrownException`, and there's a stored exception within the
822 // store to lookup tag information for, then do so here. If
823 // there's a wasm handler for this exception on the stack then
824 // that's the handler to resume to.
825 //
826 // Note that `unwind` is intentionally dropped on the floor
827 // here. We're resuming back into wasm with a normal state
828 // meaning we're no longer in an exceptional state. By doing
829 // this the internal `self.unwind` state reflects `None`.
830 //
831 // SAFETY: we are invoking `compute_handler()` while Wasm is
832 // on the stack and we have re-entered via a trampoline, as
833 // required by its stack-walking logic.
834 #[cfg(feature = "gc")]
835 if err.is::<ThrownException>()
836 && let Some((instance, tag)) = store.pending_exception_tag_and_instance()
837 && let Some(catch) = unsafe { compute_handler(store, instance, tag) }
838 {
839 handler = catch;
840 // Take the pending exception at this time and use it as
841 // payload.
842 payload1 = usize::try_from(
843 store.expose_pending_exception_to_wasm().unwrap().get(),
844 )
845 .expect("GC ref does not fit in usize");
846 payload2 = 0;
847 drop(unwind);
848 break 'done;
849 }
850
851 has_backtrace = has_backtrace || err.is::<WasmBacktrace>();
852 }
853
854 // If doesn't yet already have a backtrace, and one's not been
855 // captured yet, then assign one now.
856 if !has_backtrace {
857 trap.backtrace = self.capture_backtrace(store.vm_store_context_mut(), None);
858 trap.coredumpstack = self.capture_coredump(store.vm_store_context_mut(), None);
859 }
860 }
861
862 // If this wasn't a wasm-caught exception, then catch the exception
863 // within the original entrypoint into wasm. Note that in this
864 // situation the `unwind` value is replaced within `self` to ensure
865 // that it's picked up on the other side of the trampoline catching
866 // this error.
867 handler = entry_trap_handler(store.vm_store_context());
868 payload1 = 0;
869 payload2 = 0;
870 self.unwind.set(unwind);
871 break 'done; // be sure `'done` is considered used
872 }
873
874 unsafe {
875 self.resume_to_exception_handler(store.executor(), &handler, payload1, payload2);
876 }
877 }
878
879 pub(crate) fn entry_trap_handler(&self) -> Handler {
880 unsafe { entry_trap_handler(self.vm_store_context.get().as_ref()) }
881 }
882
883 unsafe fn resume_to_exception_handler(
884 &self,
885 executor: ExecutorRef<'_>,
886 handler: &Handler,
887 payload1: usize,
888 payload2: usize,
889 ) {
890 unsafe {
891 match executor {
892 ExecutorRef::Interpreter(mut r) => {
893 r.resume_to_exception_handler(handler, payload1, payload2)
894 }
895 #[cfg(has_host_compiler_backend)]
896 ExecutorRef::Native => handler.resume_tailcc(payload1, payload2),
897 }
898 }
899 }
900
901 fn capture_backtrace(
902 &self,
903 limits: *const VMStoreContext,
904 trap_pc_and_fp: Option<(usize, usize)>,
905 ) -> Option<Backtrace> {
906 if !self.capture_backtrace {
907 return None;
908 }
909
910 Some(unsafe { Backtrace::new_with_trap_state(limits, self.unwinder, self, trap_pc_and_fp) })
911 }
912
913 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Self> + 'a {
914 let mut state = Some(self);
915 core::iter::from_fn(move || {
916 let this = state?;
917 state = unsafe { this.prev().as_ref() };
918 Some(this)
919 })
920 }
921
922 /// Trap handler using our thread-local state.
923 ///
924 /// * `regs` - some special program registers at the time that the trap
925 /// happened, for example `pc`.
926 /// * `faulting_addr` - the system-provided address that the a fault, if
927 /// any, happened at. This is used when debug-asserting that all segfaults
928 /// are known to live within a `Store<T>` in a valid range.
929 /// * `call_handler` - a closure used to invoke the platform-specific
930 /// signal handler for each instance, if available.
931 ///
932 /// Attempts to handle the trap if it's a wasm trap. Returns a `TrapTest`
933 /// which indicates what this could be, such as:
934 ///
935 /// * `TrapTest::NotWasm` - not a wasm fault, this should get forwarded to
936 /// the next platform-specific fault handler.
937 /// * `TrapTest::HandledByEmbedder` - the embedder `call_handler` handled
938 /// this signal, nothing else to do.
939 /// * `TrapTest::Trap` - this is a wasm trap an the stack needs to be
940 /// unwound now.
941 pub(crate) fn test_if_trap(
942 &self,
943 regs: TrapRegisters,
944 faulting_addr: Option<usize>,
945 call_handler: impl FnOnce(&SignalHandler) -> bool,
946 ) -> TrapTest {
947 // First up see if any instance registered has a custom trap handler,
948 // in which case run them all. If anything handles the trap then we
949 // return that the trap was handled.
950 let _ = &call_handler;
951 #[cfg(all(has_native_signals, not(miri)))]
952 if let Some(handler) = self.signal_handler {
953 if unsafe { call_handler(&*handler) } {
954 return TrapTest::HandledByEmbedder;
955 }
956 }
957
958 // If this fault wasn't in wasm code, then it's not our problem
959 let Some((code, text_offset)) = lookup_code(regs.pc) else {
960 return TrapTest::NotWasm;
961 };
962
963 // If the fault was at a location that was not marked as potentially
964 // trapping, then that's a bug in Cranelift/Winch/etc. Don't try to
965 // catch the trap and pretend this isn't wasm so the program likely
966 // aborts.
967 let Some(trap) = code.lookup_trap_code(text_offset) else {
968 return TrapTest::NotWasm;
969 };
970
971 // If all that passed then this is indeed a wasm trap, so return the
972 // `Handler` setup in the original wasm frame.
973 self.set_jit_trap(regs, faulting_addr, trap);
974 let entry_handler = self.entry_trap_handler();
975 TrapTest::Trap(entry_handler)
976 }
977
978 pub(crate) fn set_jit_trap(
979 &self,
980 TrapRegisters { pc, fp, .. }: TrapRegisters,
981 faulting_addr: Option<usize>,
982 trap: wasmtime_environ::CompiledTrap,
983 ) {
984 let mut unwind = UnwindReason::from(TrapReason::Jit {
985 pc,
986 faulting_addr,
987 trap,
988 });
989 if let UnwindReason::Trap(Ok(trap)) = &mut unwind {
990 trap.backtrace =
991 self.capture_backtrace(self.vm_store_context.get().as_ptr(), Some((pc, fp)));
992 trap.coredumpstack =
993 self.capture_coredump(self.vm_store_context.get().as_ptr(), Some((pc, fp)));
994 }
995 self.record_unwind(unwind);
996 }
997}
998
999fn entry_trap_handler(vm_store_context: &VMStoreContext) -> Handler {
1000 unsafe {
1001 let fp = *vm_store_context.last_wasm_entry_fp.get();
1002 let sp = *vm_store_context.last_wasm_entry_sp.get();
1003 let pc = *vm_store_context.last_wasm_entry_trap_handler.get();
1004 Handler { pc, sp, fp }
1005 }
1006}
1007
1008/// A private inner module managing the state of Wasmtime's thread-local storage
1009/// (TLS) state.
1010///
1011/// Wasmtime at this time has a single pointer of TLS. This single pointer of
1012/// TLS is the totality of all TLS required by Wasmtime. By keeping this as
1013/// small as possible it generally makes it easier to integrate with external
1014/// systems and implement features such as fiber context switches. This single
1015/// TLS pointer is declared in platform-specific modules to handle platform
1016/// differences, so this module here uses getters/setters which delegate to
1017/// platform-specific implementations.
1018///
1019/// The single TLS pointer used by Wasmtime is morally
1020/// `Option<&CallThreadState>` meaning that it's a possibly-present pointer to
1021/// some state. This pointer is a pointer to the most recent (youngest)
1022/// `CallThreadState` activation, or the most recent call into WebAssembly.
1023///
1024/// This TLS pointer is additionally the head of a linked list of activations
1025/// that are all stored on the stack for the current thread. Each time
1026/// WebAssembly is recursively invoked by an embedder will push a new entry into
1027/// this linked list. This singly-linked list is maintained with its head in TLS
1028/// node pointers are stored in `CallThreadState::prev`.
1029///
1030/// An example stack might look like this:
1031///
1032/// ```text
1033/// ┌─────────────────────┐◄───── highest, or oldest, stack address
1034/// │ native stack frames │
1035/// │ ... │
1036/// │ ┌───────────────┐◄─┼──┐
1037/// │ │CallThreadState│ │ │
1038/// │ └───────────────┘ │ p
1039/// ├─────────────────────┤ r
1040/// │ wasm stack frames │ e
1041/// │ ... │ v
1042/// ├─────────────────────┤ │
1043/// │ native stack frames │ │
1044/// │ ... │ │
1045/// │ ┌───────────────┐◄─┼──┼── TLS pointer
1046/// │ │CallThreadState├──┼──┘
1047/// │ └───────────────┘ │
1048/// ├─────────────────────┤
1049/// │ wasm stack frames │
1050/// │ ... │
1051/// ├─────────────────────┤
1052/// │ native stack frames │
1053/// │ ... │
1054/// └─────────────────────┘◄───── smallest, or youngest, stack address
1055/// ```
1056///
1057/// # Fibers and async
1058///
1059/// Wasmtime supports stack-switching with fibers to implement async. This means
1060/// that Wasmtime will temporarily execute code on a separate stack and then
1061/// suspend from this stack back to the embedder for async operations. Doing
1062/// this safely requires manual management of the TLS pointer updated by
1063/// Wasmtime.
1064///
1065/// For example when a fiber is suspended that means that the TLS pointer needs
1066/// to be restored to whatever it was when the fiber was resumed. Additionally
1067/// this may need to pop multiple `CallThreadState` activations, one for each
1068/// one located on the fiber stack itself.
1069///
1070/// The `AsyncWasmCallState` and `PreviousAsyncWasmCallState` structures in this
1071/// module are used to manage this state, namely:
1072///
1073/// * The `AsyncWasmCallState` structure represents the state of a suspended
1074/// fiber. This is a linked list, in reverse order, from oldest activation on
1075/// the fiber to youngest activation on the fiber.
1076///
1077/// * The `PreviousAsyncWasmCallState` structure represents a pointer within our
1078/// thread's TLS linked list of activations when a fiber was resumed. This
1079/// pointer is used during fiber suspension to know when to stop popping
1080/// activations from the thread's linked list.
1081///
1082/// Note that this means that the directionality of linked list links is
1083/// opposite when stored in TLS vs when stored for a suspended fiber. The
1084/// thread's current list pointed to by TLS is youngest-to-oldest links, while a
1085/// suspended fiber stores oldest-to-youngest links.
1086pub(crate) mod tls {
1087 use super::CallThreadState;
1088
1089 pub use raw::Ptr;
1090
1091 // An even *more* inner module for dealing with TLS. This actually has the
1092 // thread local variable and has functions to access the variable.
1093 //
1094 // Note that this is specially done to fully encapsulate that the accessors
1095 // for tls may or may not be inlined. Wasmtime's async support employs stack
1096 // switching which can resume execution on different OS threads. This means
1097 // that borrows of our TLS pointer must never live across accesses because
1098 // otherwise the access may be split across two threads and cause unsafety.
1099 //
1100 // This also means that extra care is taken by the runtime to save/restore
1101 // these TLS values when the runtime may have crossed threads.
1102 //
1103 // Note, though, that if async support is disabled at compile time then
1104 // these functions are free to be inlined.
1105 pub(super) mod raw {
1106 use super::CallThreadState;
1107
1108 pub type Ptr = *const CallThreadState;
1109
1110 const _: () = {
1111 assert!(core::mem::align_of::<CallThreadState>() > 1);
1112 };
1113
1114 fn tls_get() -> (Ptr, bool) {
1115 let mut initialized = false;
1116 let p = crate::runtime::vm::sys::tls_get().map_addr(|a| {
1117 initialized = (a & 1) != 0;
1118 a & !1
1119 });
1120 (p.cast(), initialized)
1121 }
1122
1123 fn tls_set(ptr: Ptr, initialized: bool) {
1124 let encoded = ptr.map_addr(|a| a | usize::from(initialized));
1125 crate::runtime::vm::sys::tls_set(encoded.cast_mut().cast::<u8>());
1126 }
1127
1128 #[cfg_attr(feature = "async", inline(never))] // see module docs
1129 #[cfg_attr(not(feature = "async"), inline)]
1130 pub fn replace(val: Ptr) -> Ptr {
1131 // When a new value is configured that means that we may be
1132 // entering WebAssembly so check to see if this thread has
1133 // performed per-thread initialization for traps.
1134 let (prev, initialized) = tls_get();
1135 if !initialized {
1136 super::super::lazy_per_thread_init();
1137 }
1138 tls_set(val, true);
1139 prev
1140 }
1141
1142 /// Eagerly initialize thread-local runtime functionality. This will be performed
1143 /// lazily by the runtime if users do not perform it eagerly.
1144 #[cfg_attr(feature = "async", inline(never))] // see module docs
1145 #[cfg_attr(not(feature = "async"), inline)]
1146 pub fn initialize() {
1147 let (state, initialized) = tls_get();
1148 if initialized {
1149 return;
1150 }
1151 super::super::lazy_per_thread_init();
1152 tls_set(state, true);
1153 }
1154
1155 #[cfg_attr(feature = "async", inline(never))] // see module docs
1156 #[cfg_attr(not(feature = "async"), inline)]
1157 pub fn get() -> Ptr {
1158 tls_get().0
1159 }
1160 }
1161
1162 pub use raw::initialize as tls_eager_initialize;
1163
1164 /// Opaque state used to persist the state of the `CallThreadState`
1165 /// activations associated with a fiber stack that's used as part of an
1166 /// async wasm call.
1167 #[cfg(feature = "async")]
1168 pub struct AsyncWasmCallState {
1169 // The head of a linked list of activations that are currently present
1170 // on an async call's fiber stack. This pointer points to the oldest
1171 // activation frame where the `prev` links internally link to younger
1172 // activation frames.
1173 //
1174 // When pushed onto a thread this linked list is traversed to get pushed
1175 // onto the current thread at the time.
1176 //
1177 // If this pointer is null then that means that the fiber this state is
1178 // associated with has no activations.
1179 state: raw::Ptr,
1180 }
1181
1182 // SAFETY: This is a relatively unsafe unsafe block and not really all that
1183 // well audited. The general idea is that the linked list of activations
1184 // owned by `self.state` are safe to send to other threads, but that relies
1185 // on everything internally being safe as well as stack variables and such.
1186 // This is more-or-less tied to the very large comment in `fiber.rs` about
1187 // `unsafe impl Send` there.
1188 #[cfg(feature = "async")]
1189 unsafe impl Send for AsyncWasmCallState {}
1190
1191 #[cfg(feature = "async")]
1192 impl AsyncWasmCallState {
1193 /// Creates new state that initially starts as null.
1194 pub fn new() -> AsyncWasmCallState {
1195 AsyncWasmCallState {
1196 state: core::ptr::null_mut(),
1197 }
1198 }
1199
1200 /// Pushes the saved state of this wasm's call onto the current thread's
1201 /// state.
1202 ///
1203 /// This will iterate over the linked list of states stored within
1204 /// `self` and push them sequentially onto the current thread's
1205 /// activation list.
1206 ///
1207 /// The returned `PreviousAsyncWasmCallState` captures the state of this
1208 /// thread just before this operation, and it must have its `restore`
1209 /// method called to restore the state when the async wasm is suspended
1210 /// from.
1211 ///
1212 /// # Unsafety
1213 ///
1214 /// Must be carefully coordinated with
1215 /// `PreviousAsyncWasmCallState::restore` and fiber switches to ensure
1216 /// that this doesn't push stale data and the data is popped
1217 /// appropriately.
1218 pub unsafe fn push(self) -> PreviousAsyncWasmCallState {
1219 // First save the state of TLS as-is so when this state is popped
1220 // off later on we know where to stop.
1221 let ret = PreviousAsyncWasmCallState { state: raw::get() };
1222
1223 // The oldest activation, if present, has various `VMStoreContext`
1224 // fields saved within it. These fields were the state for the
1225 // *youngest* activation when a suspension previously happened. By
1226 // swapping them back into the store this is an O(1) way of
1227 // restoring the state of a store's metadata fields at the time of
1228 // the suspension.
1229 //
1230 // The store's previous values before this function will all get
1231 // saved in the oldest activation's state on the stack. The store's
1232 // current state then describes the youngest activation which is
1233 // restored via the loop below.
1234 unsafe {
1235 if let Some(state) = self.state.as_ref() {
1236 state.swap();
1237 }
1238 }
1239
1240 // Our `state` pointer is a linked list of oldest-to-youngest so by
1241 // pushing in order of the list we restore the youngest-to-oldest
1242 // list as stored in the state of this current thread.
1243 let mut ptr = self.state;
1244 unsafe {
1245 while let Some(state) = ptr.as_ref() {
1246 ptr = state.prev.replace(core::ptr::null_mut());
1247 state.push();
1248 }
1249 }
1250 ret
1251 }
1252
1253 /// Performs a runtime check that this state is indeed null.
1254 pub fn assert_null(&self) {
1255 assert!(self.state.is_null());
1256 }
1257
1258 /// Asserts that the current CallThreadState pointer, if present, is not
1259 /// in the `range` specified.
1260 ///
1261 /// This is used when exiting a future in Wasmtime to assert that the
1262 /// current CallThreadState pointer does not point within the stack
1263 /// we're leaving (e.g. allocated for a fiber).
1264 pub fn assert_current_state_not_in_range(range: core::ops::Range<usize>) {
1265 let p = raw::get() as usize;
1266 assert!(!range.contains(&p));
1267 }
1268 }
1269
1270 /// Opaque state used to help control TLS state across stack switches for
1271 /// async support.
1272 ///
1273 /// This structure is returned from [`AsyncWasmCallState::push`] and
1274 /// represents the state of this thread's TLS variable prior to the push
1275 /// operation.
1276 #[cfg(feature = "async")]
1277 pub struct PreviousAsyncWasmCallState {
1278 // The raw value of this thread's TLS pointer when this structure was
1279 // created. This is not dereferenced or inspected but is used to halt
1280 // linked list traversal in [`PreviousAsyncWasmCallState::restore`].
1281 state: raw::Ptr,
1282 }
1283
1284 #[cfg(feature = "async")]
1285 impl PreviousAsyncWasmCallState {
1286 /// Pops a fiber's linked list of activations and stores them in
1287 /// `AsyncWasmCallState`.
1288 ///
1289 /// This will pop the top activation of this current thread continuously
1290 /// until it reaches whatever the current activation was when
1291 /// [`AsyncWasmCallState::push`] was originally called.
1292 ///
1293 /// # Unsafety
1294 ///
1295 /// Must be paired with a `push` and only performed at a time when a
1296 /// fiber is being suspended.
1297 pub unsafe fn restore(self) -> AsyncWasmCallState {
1298 let thread_head = self.state;
1299 core::mem::forget(self);
1300 let mut ret = AsyncWasmCallState::new();
1301 loop {
1302 // If the current TLS state is as we originally found it, then
1303 // this loop is finished.
1304 //
1305 // Note, though, that before exiting, if the oldest
1306 // `CallThreadState` is present, the current state of
1307 // `VMStoreContext` is saved off within it. This will save the
1308 // current state, before this function, of `VMStoreContext`
1309 // into the `EntryStoreContext` stored with the oldest
1310 // activation. This is a bit counter-intuitive where the state
1311 // for the youngest activation is stored in the "old" state
1312 // of the oldest activation.
1313 //
1314 // What this does is restores the state of the store to just
1315 // before this async fiber was started. The fiber's state will
1316 // be entirely self-contained in the fiber itself and the
1317 // returned `AsyncWasmCallState`. Resumption above in
1318 // `AsyncWasmCallState::push` will perform the swap back into
1319 // the store to hook things up again.
1320 let ptr = raw::get();
1321 if ptr == thread_head {
1322 unsafe {
1323 if let Some(state) = ret.state.as_ref() {
1324 state.swap();
1325 }
1326 }
1327
1328 break ret;
1329 }
1330
1331 // Pop this activation from the current thread's TLS state, and
1332 // then afterwards push it onto our own linked list within this
1333 // `AsyncWasmCallState`. Note that the linked list in
1334 // `AsyncWasmCallState` is stored in reverse order so a
1335 // subsequent `push` later on pushes everything in the right
1336 // order.
1337 unsafe {
1338 (*ptr).pop();
1339 if let Some(state) = ret.state.as_ref() {
1340 (*ptr).prev.set(state);
1341 }
1342 }
1343 ret.state = ptr;
1344 }
1345 }
1346 }
1347
1348 #[cfg(feature = "async")]
1349 impl Drop for PreviousAsyncWasmCallState {
1350 fn drop(&mut self) {
1351 panic!("must be consumed with `restore`");
1352 }
1353 }
1354
1355 /// Configures thread local state such that for the duration of the
1356 /// execution of `closure` any call to `with` will yield `state`, unless
1357 /// this is recursively called again.
1358 #[inline]
1359 pub fn set<R>(state: &mut CallThreadState, closure: impl FnOnce(&CallThreadState) -> R) -> R {
1360 struct Reset<'a> {
1361 state: &'a CallThreadState,
1362 }
1363
1364 impl Drop for Reset<'_> {
1365 #[inline]
1366 fn drop(&mut self) {
1367 unsafe {
1368 self.state.pop();
1369 }
1370 }
1371 }
1372
1373 unsafe {
1374 state.push();
1375 let reset = Reset { state };
1376 closure(reset.state)
1377 }
1378 }
1379
1380 /// Returns the last pointer configured with `set` above, if any.
1381 pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R {
1382 let p = raw::get();
1383 unsafe { closure(if p.is_null() { None } else { Some(&*p) }) }
1384 }
1385}