1use core::{
2 cell::Cell,
3 ffi::c_void,
4 marker::PhantomData,
5 mem::ManuallyDrop,
6 ptr::{addr_of_mut, NonNull},
7 sync::atomic::Ordering,
8};
9use sdl3_sys::{
10 init::{SDL_IsMainThread, SDL_RunOnMainThread},
11 stdinc::{SDL_aligned_alloc, SDL_aligned_free},
12 thread::{SDL_GetCurrentThreadID, SDL_ThreadID},
13 timer::SDL_Delay,
14};
15
16#[cfg(target_has_atomic = "8")]
17type AtomicLeastU8 = core::sync::atomic::AtomicU8;
18#[cfg(all(not(target_has_atomic = "8"), target_has_atomic = "16"))]
19type AtomicLeastU8 = core::sync::atomic::AtomicU16;
20#[cfg(all(
21 not(any(target_has_atomic = "8", target_has_atomic = "16")),
22 target_has_atomic = "32"
23))]
24type AtomicLeastU8 = core::sync::atomic::AtomicU32;
25#[cfg(all(
26 not(any(
27 target_has_atomic = "8",
28 target_has_atomic = "16",
29 target_has_atomic = "32"
30 )),
31 target_has_atomic = "64"
32))]
33type AtomicLeastU8 = core::sync::atomic::AtomicU64;
34#[cfg(all(
35 not(any(
36 target_has_atomic = "8",
37 target_has_atomic = "16",
38 target_has_atomic = "32",
39 target_has_atomic = "64"
40 )),
41 target_has_atomic = "128"
42))]
43type AtomicLeastU8 = core::sync::atomic::AtomicU128;
44#[cfg(not(any(
45 target_has_atomic = "8",
46 target_has_atomic = "16",
47 target_has_atomic = "32",
48 target_has_atomic = "64",
49 target_has_atomic = "128"
50)))]
51compile_error!("no supported atomic integer type");
52
53#[derive(Clone, Copy)]
64pub struct MainThreadToken(PhantomData<*const ()>);
65
66impl MainThreadToken {
67 pub fn get() -> Option<Self> {
73 struct ThreadId(Cell<SDL_ThreadID>);
74 unsafe impl Sync for ThreadId {}
75
76 static MAIN_THREAD_ID: ThreadId = ThreadId(Cell::new(SDL_ThreadID(0)));
77 static MAIN_THREAD_ID_STATUS: AtomicLeastU8 = AtomicLeastU8::new(0);
78
79 loop {
80 match MAIN_THREAD_ID_STATUS.load(Ordering::Acquire) {
81 0 => {
82 if !unsafe { SDL_IsMainThread() } {
85 return None;
86 }
87 if MAIN_THREAD_ID_STATUS
88 .compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
89 .is_ok()
90 {
91 MAIN_THREAD_ID.0.set(SDL_GetCurrentThreadID());
92 MAIN_THREAD_ID_STATUS.store(2, Ordering::Release);
93 return Some(Self(PhantomData));
94 }
95 }
96 1 => (),
97 _ => {
98 return (MAIN_THREAD_ID.0.get() == SDL_GetCurrentThreadID())
99 .then_some(Self(PhantomData));
100 }
101 }
102 unsafe { SDL_Delay(0) }
103 }
104 }
105
106 #[track_caller]
112 pub fn assert() -> Self {
113 Self::get().expect("This operation can only be performed on the main thread")
114 }
115
116 #[track_caller]
123 #[inline(always)]
124 pub unsafe fn init() {
125 Self::assert();
126 }
127}
128
129#[repr(transparent)]
134pub struct MainThreadData<T>(T);
135
136unsafe impl<T> Send for MainThreadData<T> {}
137unsafe impl<T> Sync for MainThreadData<T> {}
138
139impl<T> Drop for MainThreadData<T> {
140 fn drop(&mut self) {
141 MainThreadToken::assert();
142 }
143}
144
145impl<T> MainThreadData<T> {
146 #[inline(always)]
150 pub fn new(_: MainThreadToken, data: T) -> Self {
151 Self(data)
152 }
153
154 #[inline(always)]
158 pub fn get(&self, _: MainThreadToken) -> &T {
159 &self.0
160 }
161
162 #[inline(always)]
166 pub fn get_mut(&mut self, _: MainThreadToken) -> &mut T {
167 &mut self.0
168 }
169
170 #[track_caller]
174 #[inline(always)]
175 pub fn assert_new(data: T) -> Self {
176 Self::new(MainThreadToken::assert(), data)
177 }
178
179 #[track_caller]
183 #[inline(always)]
184 pub fn assert_get(&self) -> &T {
185 self.get(MainThreadToken::assert())
186 }
187
188 #[track_caller]
192 #[inline(always)]
193 pub fn assert_get_mut(&mut self) -> &mut T {
194 self.get_mut(MainThreadToken::assert())
195 }
196
197 #[must_use]
207 #[inline(always)]
208 pub fn get_on_main_thread(&self, callback: impl FnOnce(&T) + Send) -> bool {
209 run_sync_on_main_thread(move || callback(&self.0))
210 }
211
212 #[must_use]
222 #[inline(always)]
223 pub fn get_mut_on_main_thread(&mut self, callback: impl FnOnce(&mut T) + Send) -> bool {
224 run_sync_on_main_thread(move || callback(&mut self.0))
225 }
226}
227
228struct CallOnceContainer<F>(Option<F>);
229
230trait CallOnce {
231 fn call_once(&mut self);
232 fn discard(&mut self);
233}
234
235impl<F: FnOnce()> CallOnce for CallOnceContainer<F> {
236 fn call_once(&mut self) {
237 if let Some(f) = self.0.take() {
238 f();
239 }
240 }
241
242 fn discard(&mut self) {
243 self.0.take();
244 }
245}
246
247#[repr(transparent)]
248struct MainThreadCallHeader(*mut dyn CallOnce);
249
250#[repr(C)]
251struct MainThreadCall<T> {
252 header: MainThreadCallHeader,
253 data: T,
254}
255
256#[must_use]
267pub fn run_sync_on_main_thread<F: FnOnce() + Send>(callback: F) -> bool {
268 unsafe extern "C" fn main_thread_fn(userdata: *mut c_void) {
269 let call_once = unsafe { &mut *(userdata as *mut &mut dyn CallOnce) };
270 call_once.call_once();
271 }
272 let mut f = CallOnceContainer(Some(callback));
273 let mut f: &mut dyn CallOnce = &mut f;
274 let f = &mut f as *mut &mut dyn CallOnce as *mut c_void;
275 unsafe { SDL_RunOnMainThread(Some(main_thread_fn), f, true) }
276}
277
278#[must_use]
287pub fn run_async_on_main_thread<F: FnOnce() + Send + 'static>(callback: F) -> bool {
288 if const { size_of::<F>() == 0 } {
291 unsafe extern "C" fn main_thread_fn<F: FnOnce() + Send + 'static>(userdata: *mut c_void) {
293 unsafe { (userdata as *mut F).read()() }
294 }
295 let callback = ManuallyDrop::new(callback);
296 let userdata: *mut F = NonNull::<F>::dangling().as_ptr();
297 if unsafe { SDL_RunOnMainThread(Some(main_thread_fn::<F>), userdata as *mut c_void, false) }
298 {
299 true
300 } else {
301 let _ = ManuallyDrop::into_inner(callback);
302 false
303 }
304 } else {
305 unsafe extern "C" fn main_thread_fn(userdata: *mut c_void) {
307 defer!(unsafe { SDL_aligned_free(userdata) });
308 unsafe { &mut *((*(userdata as *mut MainThreadCallHeader)).0) }.call_once();
309 }
310 let f = CallOnceContainer(Some(callback));
311 let userdata = unsafe {
312 SDL_aligned_alloc(
313 align_of::<MainThreadCall<CallOnceContainer<F>>>(),
314 size_of::<MainThreadCall<CallOnceContainer<F>>>(),
315 )
316 } as *mut MainThreadCall<CallOnceContainer<F>>;
317 if userdata.is_null() {
318 return false;
319 }
320 let payload = unsafe { addr_of_mut!((*userdata).data) };
321 unsafe {
322 addr_of_mut!((*userdata).header.0).write(payload as *mut dyn CallOnce);
323 (payload as *mut ManuallyDrop<CallOnceContainer<F>>).write(ManuallyDrop::new(f));
324 }
325 let userdata = userdata as *mut c_void;
326 if unsafe { SDL_RunOnMainThread(Some(main_thread_fn), userdata, false) } {
327 true
328 } else {
329 defer!(unsafe { SDL_aligned_free(userdata) });
330 unsafe { &mut *((*(userdata as *mut MainThreadCallHeader)).0) }.discard();
331 false
332 }
333 }
334}