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;