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