ferroui_base/threading/
dispatcher_exceptions.rs1use std::rc::Rc;
2
3use super::dispatcher_operation::DispatcherException;
4use super::{Dispatcher, DispatcherUnhandledExceptionEventArgs, DispatcherUnhandledExceptionFilterEventArgs};
5use crate::reactive::{Disposable, IDisposable};
6
7fn exception_key(exception: &DispatcherException) -> usize {
8 let payload: &(dyn std::any::Any + Send) = &**exception;
9 payload as *const (dyn std::any::Any + Send) as *const () as usize
10}
11
12impl Dispatcher {
13 pub fn unhandled_exception(
26 &self,
27 handler: impl for<'a> Fn(&DispatcherUnhandledExceptionEventArgs<'a>) + 'static,
28 ) -> Rc<dyn IDisposable> {
29 self.verify_access();
30 let Some(local) = self.try_local() else {
31 return Disposable::empty();
32 };
33 let token = local.unhandled_exception.add(Rc::new(handler));
34 let local = Rc::downgrade(&local);
35 Disposable::create(move || {
36 if let Some(local) = local.upgrade() {
37 local.unhandled_exception.remove(token);
38 }
39 })
40 }
41
42 pub fn unhandled_exception_filter(
53 &self,
54 handler: impl for<'a> Fn(&DispatcherUnhandledExceptionFilterEventArgs<'a>) + 'static,
55 ) -> Rc<dyn IDisposable> {
56 self.verify_access();
57 let Some(local) = self.try_local() else {
58 return Disposable::empty();
59 };
60 let token = local.unhandled_exception_filter.add(Rc::new(handler));
61 let local = Rc::downgrade(&local);
62 Disposable::create(move || {
63 if let Some(local) = local.upgrade() {
64 local.unhandled_exception_filter.remove(token);
65 }
66 })
67 }
68
69 pub(crate) fn exception_filter(&self, e: &DispatcherException) -> bool {
71 let Some(local) = self.try_local() else {
72 return false;
73 };
74
75 let key = exception_key(e);
79 {
80 let mut seen = local.seen_exceptions.borrow_mut();
81 if seen.contains(&key) {
82 return false;
84 }
85 seen.push(key);
87 }
88
89 let mut request_catch = !local.unhandled_exception.is_empty();
91
92 if !local.unhandled_exception_filter.is_empty() {
95 let dispatcher = self.to_arc();
98 let args = DispatcherUnhandledExceptionFilterEventArgs::new(&dispatcher, &**e, request_catch);
99 for (_, handler) in local.unhandled_exception_filter.snapshot().iter() {
100 handler(&args);
101 }
102 request_catch = args.request_catch();
103 }
104
105 request_catch
106 }
107
108 pub(crate) fn catch_exception(&self, e: &DispatcherException) -> bool {
109 let Some(local) = self.try_local() else {
110 return false;
111 };
112 let mut handled = false;
113
114 if !local.unhandled_exception.is_empty() {
115 let dispatcher = self.to_arc();
116 let args = DispatcherUnhandledExceptionEventArgs::new(&dispatcher, &**e, false);
117 for (_, handler) in local.unhandled_exception.snapshot().iter() {
118 handler(&args);
119 }
120 handled = args.handled();
121 }
122
123 handled
124 }
125
126 pub(crate) fn try_catch_when(&self, e: &DispatcherException) -> bool {
128 if self.exception_filter(e) {
129 if !self.catch_exception(e) {
130 return false;
131 }
132 } else {
133 return false;
134 }
135
136 self.forget_exception(e);
139 true
140 }
141
142 pub(crate) fn forget_exception(&self, e: &DispatcherException) {
144 if let Some(local) = self.try_local() {
145 let key = exception_key(e);
146 local.seen_exceptions.borrow_mut().retain(|seen| *seen != key);
147 }
148 }
149
150 pub(crate) fn forget_exception_if_outermost(&self, e: &DispatcherException) {
154 if let Some(local) = self.try_local() {
155 if local.executing_depth.get() == 0 {
156 let key = exception_key(e);
157 local.seen_exceptions.borrow_mut().retain(|seen| *seen != key);
158 }
159 }
160 }
161}