Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use core::{
9 cell::Cell,
10 sync::atomic::{AtomicUsize, Ordering},
11};
12use cpu_local::cpu_local;
13
14/// A reason for forcing an immediate abort on panic.
15#[derive(Debug)]
16pub enum MustAbort {
17 // AlwaysAbort,
18 PanicInHook,
19}
20
21// Panic count for the current thread and whether a panic hook is currently
22// being executed.
23cpu_local! {
24 static LOCAL_PANIC_COUNT: Cell<(usize, bool)> = Cell::new((0, false));
25}
26
27static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0);
28
29pub fn increase(run_panic_hook: bool) -> Option<MustAbort> {
30 let (count, in_panic_hook) = LOCAL_PANIC_COUNT.get();
31 if in_panic_hook {
32 return Some(MustAbort::PanicInHook);
33 }
34 LOCAL_PANIC_COUNT.set((count + 1, run_panic_hook));
35 None
36}
37
38pub fn finished_panic_hook() {
39 let (count, _) = LOCAL_PANIC_COUNT.get();
40 LOCAL_PANIC_COUNT.set((count, false));
41}
42
43pub fn decrease() {
44 GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
45 let (count, _) = LOCAL_PANIC_COUNT.get();
46 LOCAL_PANIC_COUNT.set((count - 1, false));
47}
48
49// Disregards ALWAYS_ABORT_FLAG
50#[must_use]
51#[inline]
52pub fn count_is_zero() -> bool {
53 if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) == 0 {
54 // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
55 // (including the current one) will have `LOCAL_PANIC_COUNT`
56 // equal to zero, so TLS access can be avoided.
57 //
58 // In terms of performance, a relaxed atomic load is similar to a normal
59 // aligned memory read (e.g., a mov instruction in x86), but with some
60 // compiler optimization restrictions. On the other hand, a TLS access
61 // might require calling a non-inlinable function (such as `__tls_get_addr`
62 // when using the GD TLS model).
63 true
64 } else {
65 is_zero_slow_path()
66 }
67}
68
69// Slow path is in a separate function to reduce the amount of code
70// inlined from `count_is_zero`.
71#[inline(never)]
72#[cold]
73fn is_zero_slow_path() -> bool {
74 LOCAL_PANIC_COUNT.get().0 == 0
75}