1use std::any::Any;
2use std::panic::{UnwindSafe, catch_unwind};
3use std::sync::atomic::{AtomicU64, Ordering};
4
5pub enum QueryAborted {
13 KeyboardInterrupt,
14 OocOutOfDisk,
15}
16
17static POLARS_ABORT_PREFIX: &str = "__POLARS_ABORT_";
19static POLARS_ABORT_KEYBOARD_INTERRUPT_STR: &str = "__POLARS_ABORT_KEYBOARD_INTERRUPT";
20static POLARS_ABORT_OOC_OUT_OF_DISK_STR: &str = "__POLARS_ABORT_OOC_OUT_OF_DISK";
21
22const ABORT_KEYBOARD_INTERRUPT_BIT: u64 = 1;
25const ABORT_OOC_OUT_OF_DISK_BIT: u64 = 2;
26const ABORT_CATCHERS_UNIT: u64 = 4;
27static ABORT_STATE: AtomicU64 = AtomicU64::new(0);
28
29fn decode_polars_abort(p: &dyn Any) -> Option<QueryAborted> {
30 let s = if let Some(s) = p.downcast_ref::<&str>() {
31 s
32 } else if let Some(s) = p.downcast_ref::<String>() {
33 s.as_str()
34 } else {
35 return None;
36 };
37
38 if !s.contains(POLARS_ABORT_PREFIX) {
39 return None;
40 }
41
42 if s.contains(POLARS_ABORT_KEYBOARD_INTERRUPT_STR) {
43 Some(QueryAborted::KeyboardInterrupt)
44 } else if s.contains(POLARS_ABORT_OOC_OUT_OF_DISK_STR) {
45 Some(QueryAborted::OocOutOfDisk)
46 } else {
47 unreachable!()
48 }
49}
50
51pub fn register_polars_abort_mechanism() {
52 let default_hook = std::panic::take_hook();
53 std::panic::set_hook(Box::new(move |p| {
54 let num_catchers =
57 ABORT_STATE.load(Ordering::Relaxed) >> ABORT_CATCHERS_UNIT.trailing_zeros();
58 let suppress = num_catchers > 0 && decode_polars_abort(p.payload()).is_some();
59 if !suppress {
60 default_hook(p);
61 }
62 }));
63
64 #[cfg(not(target_family = "wasm"))]
66 unsafe {
67 signal_hook::low_level::register(signal_hook::consts::signal::SIGINT, move || {
69 ABORT_STATE
71 .fetch_update(Ordering::Release, Ordering::Relaxed, |state| {
72 let num_catchers = state >> ABORT_CATCHERS_UNIT.trailing_zeros();
73 if num_catchers > 0 {
74 Some(state | ABORT_KEYBOARD_INTERRUPT_BIT)
75 } else {
76 None
77 }
78 })
79 .ok();
80 })
81 .unwrap();
82 }
83}
84
85pub fn polars_abort_ooc_out_of_disk() -> ! {
86 ABORT_STATE
87 .fetch_update(Ordering::Release, Ordering::Relaxed, |state| {
88 let num_catchers = state >> ABORT_CATCHERS_UNIT.trailing_zeros();
89 if num_catchers > 0 {
90 Some(state | ABORT_OOC_OUT_OF_DISK_BIT)
91 } else {
92 None
93 }
94 })
95 .ok();
96
97 std::panic::panic_any(POLARS_ABORT_OOC_OUT_OF_DISK_STR);
98}
99
100#[inline(always)]
102pub fn try_raise_polars_abort() {
103 if ABORT_STATE.load(Ordering::Acquire) & (ABORT_CATCHERS_UNIT - 1) != 0 {
104 try_raise_polars_abort_slow()
105 }
106}
107
108#[inline(never)]
109#[cold]
110fn try_raise_polars_abort_slow() {
111 let state = ABORT_STATE.load(Ordering::Acquire);
112 if state & ABORT_KEYBOARD_INTERRUPT_BIT != 0 {
113 std::panic::panic_any(POLARS_ABORT_KEYBOARD_INTERRUPT_STR);
114 } else if state & ABORT_OOC_OUT_OF_DISK_BIT != 0 {
115 std::panic::panic_any(POLARS_ABORT_OOC_OUT_OF_DISK_STR);
116 } else {
117 unreachable!()
118 }
119}
120
121pub fn catch_polars_abort<R, F: FnOnce() -> R + UnwindSafe>(try_fn: F) -> Result<R, QueryAborted> {
124 try_register_catcher()?;
127 let ret = catch_unwind(try_fn);
128 unregister_catcher();
129 ret.map_err(|p| {
130 if let Some(reason) = decode_polars_abort(&*p) {
131 reason
132 } else {
133 std::panic::resume_unwind(p)
134 }
135 })
136}
137
138fn try_register_catcher() -> Result<(), QueryAborted> {
139 let old_state = ABORT_STATE.fetch_add(ABORT_CATCHERS_UNIT, Ordering::Relaxed);
140 if old_state & (ABORT_CATCHERS_UNIT - 1) != 0 {
141 unregister_catcher();
142
143 return if old_state & ABORT_KEYBOARD_INTERRUPT_BIT != 0 {
144 Err(QueryAborted::KeyboardInterrupt)
145 } else if old_state & ABORT_OOC_OUT_OF_DISK_BIT != 0 {
146 Err(QueryAborted::OocOutOfDisk)
147 } else {
148 unreachable!()
149 };
150 }
151 Ok(())
152}
153
154fn unregister_catcher() {
155 ABORT_STATE
156 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |state| {
157 let num_catchers = state >> ABORT_CATCHERS_UNIT.trailing_zeros();
158 if num_catchers > 1 {
159 Some(state - ABORT_CATCHERS_UNIT)
160 } else {
161 Some(0)
163 }
164 })
165 .ok();
166}