1use std::marker::PhantomData;
13
14use super::vtable::call_vtable;
15
16#[repr(C)]
18pub struct EventDispatcher<H> {
19 _opaque: [u8; 0],
20 _handler: PhantomData<*mut H>,
21}
22
23pub mod priority {
25 pub const HIGHEST: i8 = -128;
27 pub const FAIRLY_HIGH: i8 = -64;
29 pub const DEFAULT: i8 = 0;
31 pub const FAIRLY_LOW: i8 = 63;
33 pub const LOWEST: i8 = 127;
35}
36
37const SLOT_ADD: usize = 0;
38const SLOT_REMOVE: usize = 1;
39const SLOT_COUNT: usize = 3;
40
41pub unsafe fn add_event_handler<H>(
48 dispatcher: *mut EventDispatcher<H>,
49 handler: *mut H,
50 priority: i8,
51) -> bool {
52 call_vtable!(
53 dispatcher.cast::<u8>(),
54 0,
55 SLOT_ADD,
56 (*mut u8, i8) -> bool,
58 (handler.cast::<u8>(), priority),
59 false
60 )
61}
62
63pub unsafe fn remove_event_handler<H>(
68 dispatcher: *mut EventDispatcher<H>,
69 handler: *mut H,
70) -> bool {
71 call_vtable!(
72 dispatcher.cast::<u8>(),
73 0,
74 SLOT_REMOVE,
75 (*mut u8) -> bool,
76 (handler.cast::<u8>()),
77 false
78 )
79}
80
81#[must_use]
86pub unsafe fn event_handler_count<H>(dispatcher: *mut EventDispatcher<H>) -> usize {
87 call_vtable!(dispatcher.cast::<u8>(), 0, SLOT_COUNT, () -> usize, (), 0)
88}
89
90macro_rules! event_handler {
102 (
104 $(#[$meta:meta])*
105 $name:ident for $handler:ident {
106 $(
107 $(#[$fmeta:meta])*
108 $field:ident: fn($($arg:ty),* $(,)?) $(-> $ret:ty)? = $default:expr
109 ),* $(,)?
110 }
111 ) => {
112 $crate::omp::dispatch::event_handler!(@types
113 $(#[$meta])* $name for $handler {
114 $($(#[$fmeta])* $field: fn($($arg),*) $(-> $ret)?),*
115 }
116 );
117
118 impl $name {
119 pub const DEFAULT: Self = Self {
121 $($field: {
122 #[cfg(not(target_env = "msvc"))]
123 unsafe extern "C" fn f(_: *mut $handler, $(_: $arg),*) $(-> $ret)? { $default }
124 #[cfg(target_env = "msvc")]
125 unsafe extern "thiscall" fn f(_: *mut $handler, $(_: $arg),*) $(-> $ret)? { $default }
126 f
127 }),*
128 };
129 }
130 };
131 (
133 $(#[$meta:meta])*
134 $name:ident for $handler:ident {
135 $(
136 $(#[$fmeta:meta])*
137 $field:ident: fn($($arg:ty),* $(,)?) $(-> $ret:ty)?
138 ),* $(,)?
139 }
140 ) => {
141 $crate::omp::dispatch::event_handler!(@types
142 $(#[$meta])* $name for $handler {
143 $($(#[$fmeta])* $field: fn($($arg),*) $(-> $ret)?),*
144 }
145 );
146 };
147 (@types
148 $(#[$meta:meta])*
149 $name:ident for $handler:ident {
150 $(
151 $(#[$fmeta:meta])*
152 $field:ident: fn($($arg:ty),*) $(-> $ret:ty)?
153 ),*
154 }
155 ) => {
156 $(#[$meta])*
157 #[cfg(not(target_env = "msvc"))]
158 #[repr(C)]
159 #[derive(Clone, Copy)]
160 pub struct $name {
161 $($(#[$fmeta])* pub $field: unsafe extern "C" fn(*mut $handler, $($arg),*) $(-> $ret)?),*
162 }
163
164 $(#[$meta])*
165 #[cfg(target_env = "msvc")]
166 #[repr(C)]
167 #[derive(Clone, Copy)]
168 pub struct $name {
169 $($(#[$fmeta])* pub $field: unsafe extern "thiscall" fn(*mut $handler, $($arg),*) $(-> $ret)?),*
170 }
171
172 #[doc = stringify!($name)]
174 #[repr(C)]
177 pub struct $handler {
178 vtable: *const $name,
179 }
180
181 unsafe impl Send for $handler {}
183 unsafe impl Sync for $handler {}
184
185 impl $handler {
186 #[must_use]
188 pub const fn new(vtable: *const $name) -> Self {
189 Self { vtable }
190 }
191 }
192 };
193}
194
195pub(crate) use event_handler;
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use crate::omp::vtable::MockTable;
201
202 struct Probe;
203
204 #[cfg(not(target_env = "msvc"))]
205 unsafe extern "C" fn add(_: *mut u8, handler: *mut Probe, priority: i8) -> u32 {
206 u32::from(!handler.is_null() && priority == priority::FAIRLY_LOW)
208 }
209 #[cfg(target_env = "msvc")]
210 unsafe extern "thiscall" fn add(_: *mut u8, handler: *mut Probe, priority: i8) -> u32 {
211 u32::from(!handler.is_null() && priority == priority::FAIRLY_LOW)
213 }
214 #[cfg(not(target_env = "msvc"))]
215 unsafe extern "C" fn count(_: *mut u8) -> usize {
216 7
217 }
218 #[cfg(target_env = "msvc")]
219 unsafe extern "thiscall" fn count(_: *mut u8) -> usize {
220 7
221 }
222
223 static TABLE: MockTable<4> = MockTable([
224 add as *const (),
225 std::ptr::null(),
226 std::ptr::null(),
227 count as *const (),
228 ]);
229
230 #[test]
231 fn calls_reach_their_slots() {
232 let mut object = [&raw const TABLE.0 as *const ()];
233 let dispatcher = (&raw mut object).cast::<EventDispatcher<Probe>>();
234 let mut probe = Probe;
235 unsafe {
236 assert!(add_event_handler(
237 dispatcher,
238 &raw mut probe,
239 priority::FAIRLY_LOW
240 ));
241 assert_eq!(event_handler_count(dispatcher), 7);
242 assert!(!remove_event_handler(dispatcher, &raw mut probe));
244 }
245 }
246
247 #[test]
248 fn a_null_dispatcher_answers_the_defaults() {
249 let dispatcher = std::ptr::null_mut::<EventDispatcher<Probe>>();
250 unsafe {
251 assert!(!add_event_handler(dispatcher, std::ptr::null_mut(), 0));
252 assert_eq!(event_handler_count(dispatcher), 0);
253 }
254 }
255}