playdate_sys/sys/
proc.rs

1//! Process API. Abort and abort with error message.
2
3/// Executes the undefined instruction (UDF) and causes a CPU-level exception.
4/// See [`core::intrinsics::abort()`].
5#[track_caller]
6#[inline(always)]
7pub fn abort() -> ! { core::intrinsics::abort() }
8
9
10/// Stops the program execution with custom system-level error.
11///
12/// In case of missed [`crate::sys::API`] (doesn't set) uses [`abort`].
13#[track_caller]
14pub fn error<S: AsRef<str>>(text: S) -> ! {
15	#[cfg(not(playdate))]
16	{
17		use core::sync::atomic::AtomicBool;
18		use core::sync::atomic::Ordering;
19
20		// This is unreachable on the device,
21		// `API.system.error` interrupts the execution.
22		// But simulator doesn't stops.
23		// So, instead of spin-loop just save the flag and use later in the event-handler.
24
25		#[no_mangle]
26		static END_WITH_ERR: AtomicBool = AtomicBool::new(false);
27
28		END_WITH_ERR.store(true, Ordering::Relaxed);
29	}
30
31
32	if let Some(f) = unsafe { (*(*crate::sys::API).system).error } {
33		// Casting fn->void to fn->!
34		// This ugly cast is safe because of `!` is a magic compile-time marker, not a type.
35		let f: unsafe extern "C" fn(*const core::ffi::c_char, ...) = f;
36		let p =
37			core::ptr::addr_of!(f) as *const _ as *const unsafe extern "C" fn(*const core::ffi::c_char, ...) -> !;
38		let f = unsafe { p.as_ref() }.unwrap();
39
40		if let Ok(s) = alloc::ffi::CString::new(text.as_ref()) {
41			unsafe { f(s.as_ptr() as _) }
42		} else {
43			unsafe { f(text.as_ref().as_ptr() as _) }
44		}
45	} else {
46		// In case of `crate::sys::API` is missed (doesn't set) just abort the process.
47		abort()
48	}
49}