devela/code/result/panic/
define.rs

1// devela::code::result::panic::define
2//
3//! Defines the [`define_panic_handler!`] macro.
4//
5
6/// Defines a panic handler based on the chosen strategy.
7///
8/// - `loop`: Enters an infinite loop, ensuring the program halts without undefined behavior.
9/// - `unreachable`: optimally halts execution based on the target architecture.
10///   - `wasm32`: Uses `unreachable()` to signal an unrecoverable state.
11///   - `x86_64`: Uses `_mm_pause()` to reduce CPU power consumption.
12///   - `aarch64`: Uses `__nop()` as a minimal halt.
13///   - `riscv64`: Uses `wfi` (wait-for-interrupt) to idle the core.
14///   - **Fallback:** Uses an infinite loop.
15///   - (It uses `unsafe`, except for `wasm32` and the fallback.
16/// - `web_api`: Logs panic info to the Web console. It requires the `js` feature.
17///   - Accepts the size of the log buffer size in bytes. Defaults to `1024` bytes.
18/// - `custom`: Uses a user-provided function as the panic handler.
19#[macro_export]
20#[cfg_attr(cargo_primary_package, doc(hidden))]
21macro_rules! define_panic_handler {
22    (loop) => {
23        #[panic_handler]
24        fn panic(_info: &::core::panic::PanicInfo) -> ! {
25            loop {}
26        }
27    };
28    (unreachable) => {
29        #[panic_handler]
30        #[allow(unreachable_code, reason = "alternative architectures")]
31        fn panic(_info: &::core::panic::PanicInfo) -> ! {
32            // Signals an unrecoverable state to the WebAssembly runtime
33            // https://doc.rust-lang.org/core/arch/wasm32/fn.unreachable.html
34            #[cfg(target_arch = "wasm32")]
35            ::core::arch::wasm32::unreachable();
36
37            // Low-power pause instruction, avoids busy loops
38            // https://doc.rust-lang.org/core/arch/x86_64/fn._mm_pause.html
39            #[cfg(target_arch = "x86_64")]
40            unsafe {
41                ::core::arch::x86_64::_mm_pause();
42            }
43
44            // No-op (alternative could be `wfe` for efficiency)
45            // https://doc.rust-lang.org/core/arch/aarch64/fn.__nop.html
46            // https://doc.rust-lang.org/core/arch/aarch64/fn.__wfe.html (experimental)
47            #[cfg(target_arch = "aarch64")]
48            unsafe {
49                ::core::arch::aarch64::__nop();
50            }
51
52            // Wait for interrupt (low-power equivalent)
53            #[cfg(target_arch = "riscv64")]
54            unsafe {
55                ::core::arch::asm!("wfi");
56            }
57
58            loop {} // Always fallback to avoid UB if nothing else applies
59        }
60    };
61    (web_api) => {
62        $crate::define_panic_handler!(web_api: 1024);
63    };
64    (web_api: $buffer_bytes:literal) => {
65        #[panic_handler]
66        fn panic(info: &::core::panic::PanicInfo) -> ! {
67            #[cfg(target_arch = "wasm32")]
68            {
69                let mut buf = [0u8; $buffer_bytes];
70
71                // Extract and log the panic message
72                $crate::Js::console_group("#[panic_handler]");
73                match format_buf![&mut buf, "{}", info.message()] {
74                    Ok(msg_str) => $crate::Js::console_debug(msg_str),
75                    Err(truncated) => {
76                        $crate::Js::console_debug(truncated);
77                        $crate::Js::console_warn("Panic message was truncated!");
78                    }
79                }
80
81                // Extract and log the panic location
82                match info.location()
83                    .map(|loc| format_buf![&mut buf, "At {}:{}:{}",
84                        loc.file(), loc.line(), loc.column()])
85                    .unwrap_or(Ok("<panic location unknown>".into()))
86                {
87                    Ok(loc_str) => $crate::Js::console_debug(loc_str),
88                    Err(truncated) => {
89                        $crate::Js::console_debug(truncated);
90                        $crate::Js::console_warn("Panic location was truncated!");
91                    }
92                }
93                $crate::Js::console_group_end();
94
95                ::core::arch::wasm32::unreachable();
96            }
97            #[cfg(not(target_arch = "wasm32"))]
98            loop {}
99        }
100    };
101    (custom, $func:path) => {
102        #[panic_handler]
103        fn panic(info: &::core::panic::PanicInfo) -> ! {
104            $func(info)
105        }
106    };
107}
108#[doc(inline)]
109pub use define_panic_handler;