Skip to main content

azul_layout/
thread.rs

1//! Thread callback information and utilities for azul-layout
2//!
3//! This module provides thread-related callback structures for background tasks
4//! that need to interact with the UI thread and query layout information.
5
6#[cfg(feature = "std")]
7use alloc::sync::Arc;
8#[cfg(feature = "std")]
9use core::sync::atomic::AtomicBool;
10#[cfg(feature = "std")]
11use core::sync::atomic::Ordering;
12#[cfg(feature = "std")]
13use std::sync::{
14    mpsc::{channel, Receiver, Sender},
15    Mutex,
16};
17#[cfg(feature = "std")]
18use std::thread::{self, JoinHandle};
19
20use azul_core::{
21    callbacks::Update,
22    refany::{OptionRefAny, RefAny},
23    task::{
24        CheckThreadFinishedCallback, CheckThreadFinishedCallbackType, LibrarySendThreadMsgCallback,
25        LibrarySendThreadMsgCallbackType, OptionThreadSendMsg, ThreadId, ThreadReceiver,
26        ThreadReceiverDestructorCallback, ThreadReceiverInner, ThreadRecvCallback, ThreadSendMsg,
27    },
28};
29
30use crate::callbacks::CallbackInfo;
31
32// Types that need to be defined locally (not in azul-core)
33
34/// Message that is sent back from the running thread to the main thread
35#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
36#[repr(C, u8)]
37pub enum ThreadReceiveMsg {
38    WriteBack(ThreadWriteBackMsg),
39    Update(Update),
40}
41
42#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
43#[repr(C, u8)]
44pub enum OptionThreadReceiveMsg {
45    None,
46    Some(ThreadReceiveMsg),
47}
48
49impl From<Option<ThreadReceiveMsg>> for OptionThreadReceiveMsg {
50    fn from(inner: Option<ThreadReceiveMsg>) -> Self {
51        match inner {
52            None => OptionThreadReceiveMsg::None,
53            Some(v) => OptionThreadReceiveMsg::Some(v),
54        }
55    }
56}
57
58impl OptionThreadReceiveMsg {
59    pub fn into_option(self) -> Option<ThreadReceiveMsg> {
60        match self {
61            OptionThreadReceiveMsg::None => None,
62            OptionThreadReceiveMsg::Some(v) => Some(v),
63        }
64    }
65
66    pub fn as_ref(&self) -> Option<&ThreadReceiveMsg> {
67        match self {
68            OptionThreadReceiveMsg::None => None,
69            OptionThreadReceiveMsg::Some(v) => Some(v),
70        }
71    }
72}
73
74/// Message containing writeback data and callback
75#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
76#[repr(C)]
77pub struct ThreadWriteBackMsg {
78    pub refany: RefAny,
79    pub callback: WriteBackCallback,
80}
81
82impl ThreadWriteBackMsg {
83    pub fn new<C: Into<WriteBackCallback>>(callback: C, data: RefAny) -> Self {
84        Self {
85            refany: data,
86            callback: callback.into(),
87        }
88    }
89}
90
91/// ThreadSender allows sending messages from the background thread to the main thread
92#[derive(Debug)]
93#[repr(C)]
94pub struct ThreadSender {
95    #[cfg(feature = "std")]
96    pub ptr: alloc::boxed::Box<Arc<Mutex<ThreadSenderInner>>>,
97    #[cfg(not(feature = "std"))]
98    pub ptr: *const core::ffi::c_void,
99    pub run_destructor: bool,
100    /// For FFI: stores the foreign callable (e.g., PyFunction)
101    pub ctx: OptionRefAny,
102}
103
104impl Clone for ThreadSender {
105    fn clone(&self) -> Self {
106        Self {
107            ptr: self.ptr.clone(),
108            run_destructor: true,
109            ctx: self.ctx.clone(),
110        }
111    }
112}
113
114impl Drop for ThreadSender {
115    fn drop(&mut self) {
116        self.run_destructor = false;
117    }
118}
119
120impl ThreadSender {
121    #[cfg(not(feature = "std"))]
122    pub fn new(_t: ThreadSenderInner) -> Self {
123        Self {
124            ptr: core::ptr::null(),
125            run_destructor: false,
126            ctx: OptionRefAny::None,
127        }
128    }
129
130    #[cfg(feature = "std")]
131    pub fn new(t: ThreadSenderInner) -> Self {
132        Self {
133            ptr: alloc::boxed::Box::new(Arc::new(Mutex::new(t))),
134            run_destructor: true,
135            ctx: OptionRefAny::None,
136        }
137    }
138
139    /// Get the FFI context (e.g., Python callable)
140    pub fn get_ctx(&self) -> OptionRefAny {
141        self.ctx.clone()
142    }
143
144    #[cfg(not(feature = "std"))]
145    pub fn send(&mut self, _msg: ThreadReceiveMsg) -> bool {
146        false
147    }
148
149    #[cfg(feature = "std")]
150    pub fn send(&mut self, msg: ThreadReceiveMsg) -> bool {
151        let ts = match self.ptr.lock().ok() {
152            Some(s) => s,
153            None => return false,
154        };
155        (ts.send_fn.cb)(ts.ptr.as_ref() as *const _ as *const core::ffi::c_void, msg)
156    }
157}
158
159#[derive(Debug)]
160#[cfg_attr(not(feature = "std"), derive(PartialEq, PartialOrd, Eq, Ord))]
161#[repr(C)]
162pub struct ThreadSenderInner {
163    #[cfg(feature = "std")]
164    pub ptr: alloc::boxed::Box<Sender<ThreadReceiveMsg>>,
165    #[cfg(not(feature = "std"))]
166    pub ptr: *const core::ffi::c_void,
167    pub send_fn: ThreadSendCallback,
168    pub destructor: ThreadSenderDestructorCallback,
169}
170
171#[cfg(not(feature = "std"))]
172unsafe impl Send for ThreadSenderInner {}
173
174#[cfg(feature = "std")]
175impl core::hash::Hash for ThreadSenderInner {
176    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
177        (self.ptr.as_ref() as *const _ as usize).hash(state);
178    }
179}
180
181#[cfg(feature = "std")]
182impl PartialEq for ThreadSenderInner {
183    fn eq(&self, other: &Self) -> bool {
184        (self.ptr.as_ref() as *const _ as usize) == (other.ptr.as_ref() as *const _ as usize)
185    }
186}
187
188#[cfg(feature = "std")]
189impl Eq for ThreadSenderInner {}
190
191#[cfg(feature = "std")]
192impl PartialOrd for ThreadSenderInner {
193    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
194        Some(
195            (self.ptr.as_ref() as *const _ as usize)
196                .cmp(&(other.ptr.as_ref() as *const _ as usize)),
197        )
198    }
199}
200
201#[cfg(feature = "std")]
202impl Ord for ThreadSenderInner {
203    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
204        (self.ptr.as_ref() as *const _ as usize).cmp(&(other.ptr.as_ref() as *const _ as usize))
205    }
206}
207
208impl Drop for ThreadSenderInner {
209    fn drop(&mut self) {
210        (self.destructor.cb)(self);
211    }
212}
213
214/// Callback for sending messages from thread to main thread
215pub type ThreadSendCallbackType = extern "C" fn(*const core::ffi::c_void, ThreadReceiveMsg) -> bool;
216
217#[repr(C)]
218pub struct ThreadSendCallback {
219    pub cb: ThreadSendCallbackType,
220}
221
222impl core::fmt::Debug for ThreadSendCallback {
223    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
224        write!(f, "ThreadSendCallback {{ cb: {:p} }}", self.cb as *const ())
225    }
226}
227
228impl Clone for ThreadSendCallback {
229    fn clone(&self) -> Self {
230        Self { cb: self.cb }
231    }
232}
233
234impl PartialEq for ThreadSendCallback {
235    fn eq(&self, other: &Self) -> bool {
236        self.cb as usize == other.cb as usize
237    }
238}
239
240impl Eq for ThreadSendCallback {}
241
242impl PartialOrd for ThreadSendCallback {
243    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
244        Some(self.cmp(other))
245    }
246}
247
248impl Ord for ThreadSendCallback {
249    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
250        (self.cb as usize).cmp(&(other.cb as usize))
251    }
252}
253
254/// Destructor callback for ThreadSender
255pub type ThreadSenderDestructorCallbackType = extern "C" fn(*mut ThreadSenderInner);
256
257#[repr(C)]
258pub struct ThreadSenderDestructorCallback {
259    pub cb: ThreadSenderDestructorCallbackType,
260}
261
262impl core::fmt::Debug for ThreadSenderDestructorCallback {
263    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
264        write!(
265            f,
266            "ThreadSenderDestructorCallback {{ cb: {:p} }}",
267            self.cb as *const ()
268        )
269    }
270}
271
272impl Clone for ThreadSenderDestructorCallback {
273    fn clone(&self) -> Self {
274        Self { cb: self.cb }
275    }
276}
277
278impl PartialEq for ThreadSenderDestructorCallback {
279    fn eq(&self, other: &Self) -> bool {
280        self.cb as usize == other.cb as usize
281    }
282}
283
284impl Eq for ThreadSenderDestructorCallback {}
285
286impl PartialOrd for ThreadSenderDestructorCallback {
287    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
288        Some(self.cmp(other))
289    }
290}
291
292impl Ord for ThreadSenderDestructorCallback {
293    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
294        (self.cb as usize).cmp(&(other.cb as usize))
295    }
296}
297
298/// Callback that runs when a thread receives a `WriteBack` message
299///
300/// This callback runs on the main UI thread and has access dir_to:
301/// - The thread's original data
302/// - Data sent back from the background thread
303/// - Full CallbackInfo for DOM queries and UI updates
304pub type WriteBackCallbackType = extern "C" fn(
305    /* original thread data */ RefAny,
306    /* data to write back */ RefAny,
307    /* callback info */ CallbackInfo,
308) -> Update;
309
310/// Callback that can run when a thread receives a `WriteBack` message
311#[repr(C)]
312pub struct WriteBackCallback {
313    pub cb: WriteBackCallbackType,
314    /// For FFI: stores the foreign callable (e.g., PyFunction)
315    /// Native Rust code sets this to None
316    pub ctx: OptionRefAny,
317}
318
319impl WriteBackCallback {
320    /// Create a new WriteBackCallback
321    pub fn new(cb: WriteBackCallbackType) -> Self {
322        Self {
323            cb,
324            ctx: OptionRefAny::None,
325        }
326    }
327
328    /// Invoke the callback
329    pub fn invoke(
330        &self,
331        thread_data: RefAny,
332        writeback_data: RefAny,
333        callback_info: CallbackInfo,
334    ) -> Update {
335        (self.cb)(thread_data, writeback_data, callback_info)
336    }
337}
338
339impl core::fmt::Debug for WriteBackCallback {
340    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
341        write!(f, "WriteBackCallback {{ cb: {:p} }}", self.cb as *const ())
342    }
343}
344
345impl Clone for WriteBackCallback {
346    fn clone(&self) -> Self {
347        Self {
348            cb: self.cb,
349            ctx: self.ctx.clone(),
350        }
351    }
352}
353
354impl From<WriteBackCallbackType> for WriteBackCallback {
355    fn from(cb: WriteBackCallbackType) -> Self {
356        Self {
357            cb,
358            ctx: OptionRefAny::None,
359        }
360    }
361}
362
363impl PartialEq for WriteBackCallback {
364    fn eq(&self, other: &Self) -> bool {
365        self.cb as usize == other.cb as usize
366    }
367}
368
369impl Eq for WriteBackCallback {}
370
371impl PartialOrd for WriteBackCallback {
372    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
373        (self.cb as usize).partial_cmp(&(other.cb as usize))
374    }
375}
376
377impl Ord for WriteBackCallback {
378    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
379        (self.cb as usize).cmp(&(other.cb as usize))
380    }
381}
382
383impl core::hash::Hash for WriteBackCallback {
384    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
385        (self.cb as usize).hash(state);
386    }
387}
388
389// ThreadCallback type
390pub type ThreadCallbackType = extern "C" fn(RefAny, ThreadSender, ThreadReceiver);
391
392#[repr(C)]
393pub struct ThreadCallback {
394    pub cb: ThreadCallbackType,
395    /// For FFI: stores the foreign callable (e.g., PyFunction)
396    /// Native Rust code sets this to None
397    pub ctx: OptionRefAny,
398}
399
400impl ThreadCallback {
401    /// Create a new ThreadCallback
402    pub fn new(cb: ThreadCallbackType) -> Self {
403        Self {
404            cb,
405            ctx: OptionRefAny::None,
406        }
407    }
408}
409
410impl core::fmt::Debug for ThreadCallback {
411    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
412        write!(f, "ThreadCallback {{ cb: {:p} }}", self.cb as *const ())
413    }
414}
415
416impl Clone for ThreadCallback {
417    fn clone(&self) -> Self {
418        Self {
419            cb: self.cb,
420            ctx: self.ctx.clone(),
421        }
422    }
423}
424
425impl From<ThreadCallbackType> for ThreadCallback {
426    fn from(cb: ThreadCallbackType) -> Self {
427        Self {
428            cb,
429            ctx: OptionRefAny::None,
430        }
431    }
432}
433
434impl PartialEq for ThreadCallback {
435    fn eq(&self, other: &Self) -> bool {
436        self.cb as usize == other.cb as usize
437    }
438}
439
440impl Eq for ThreadCallback {}
441
442impl PartialOrd for ThreadCallback {
443    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
444        (self.cb as usize).partial_cmp(&(other.cb as usize))
445    }
446}
447
448impl Ord for ThreadCallback {
449    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
450        (self.cb as usize).cmp(&(other.cb as usize))
451    }
452}
453
454impl core::hash::Hash for ThreadCallback {
455    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
456        (self.cb as usize).hash(state);
457    }
458}
459
460pub type LibraryReceiveThreadMsgCallbackType =
461    extern "C" fn(*const core::ffi::c_void) -> OptionThreadReceiveMsg;
462
463#[repr(C)]
464pub struct LibraryReceiveThreadMsgCallback {
465    pub cb: LibraryReceiveThreadMsgCallbackType,
466}
467
468impl core::fmt::Debug for LibraryReceiveThreadMsgCallback {
469    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
470        write!(
471            f,
472            "LibraryReceiveThreadMsgCallback {{ cb: {:p} }}",
473            self.cb as *const ()
474        )
475    }
476}
477
478impl Clone for LibraryReceiveThreadMsgCallback {
479    fn clone(&self) -> Self {
480        Self { cb: self.cb }
481    }
482}
483
484pub type ThreadDestructorCallbackType = extern "C" fn(*mut ThreadInner);
485
486#[repr(C)]
487pub struct ThreadDestructorCallback {
488    pub cb: ThreadDestructorCallbackType,
489}
490
491impl core::fmt::Debug for ThreadDestructorCallback {
492    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
493        write!(
494            f,
495            "ThreadDestructorCallback {{ cb: {:p} }}",
496            self.cb as *const ()
497        )
498    }
499}
500
501impl Clone for ThreadDestructorCallback {
502    fn clone(&self) -> Self {
503        Self { cb: self.cb }
504    }
505}
506
507/// Wrapper around Thread because Thread needs to be clone-able
508#[derive(Debug)]
509#[repr(C)]
510pub struct Thread {
511    #[cfg(feature = "std")]
512    pub ptr: alloc::boxed::Box<Arc<Mutex<ThreadInner>>>,
513    #[cfg(not(feature = "std"))]
514    pub ptr: *const core::ffi::c_void,
515    pub run_destructor: bool,
516}
517
518impl Clone for Thread {
519    fn clone(&self) -> Self {
520        Self {
521            ptr: self.ptr.clone(),
522            run_destructor: true,
523        }
524    }
525}
526
527impl Drop for Thread {
528    fn drop(&mut self) {
529        self.run_destructor = false;
530    }
531}
532
533impl Thread {
534    #[cfg(feature = "std")]
535    pub fn new(ti: ThreadInner) -> Self {
536        Self {
537            ptr: alloc::boxed::Box::new(Arc::new(Mutex::new(ti))),
538            run_destructor: true,
539        }
540    }
541
542    #[cfg(not(feature = "std"))]
543    pub fn new(_ti: ThreadInner) -> Self {
544        Self {
545            ptr: core::ptr::null(),
546            run_destructor: false,
547        }
548    }
549
550    /// Creates a new thread that will execute the given callback function.
551    ///
552    /// # Arguments
553    /// * `thread_initialize_data` - Data passed to the callback when the thread starts
554    /// * `writeback_data` - Data that will be passed back when writeback messages are received
555    /// * `callback` - The callback to execute in the background thread
556    ///
557    /// # Returns
558    /// A new Thread handle that can be added to the event loop with `CallbackInfo::add_thread`
559    pub fn create<C: Into<ThreadCallback>>(
560        thread_initialize_data: RefAny,
561        writeback_data: RefAny,
562        callback: C,
563    ) -> Self {
564        create_thread_libstd(thread_initialize_data, writeback_data, callback.into())
565    }
566}
567
568/// A `Thread` is a separate thread that is owned by the framework.
569///
570/// In difference to a regular thread, you don't have to `await()` the result,
571/// you can just hand the Thread to the framework and it will automatically
572/// update the UI when the Thread is finished.
573#[derive(Debug)]
574#[repr(C)]
575pub struct ThreadInner {
576    #[cfg(feature = "std")]
577    pub thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>>,
578    #[cfg(not(feature = "std"))]
579    pub thread_handle: *const core::ffi::c_void,
580
581    #[cfg(feature = "std")]
582    pub sender: alloc::boxed::Box<Sender<ThreadSendMsg>>,
583    #[cfg(not(feature = "std"))]
584    pub sender: *const core::ffi::c_void,
585
586    #[cfg(feature = "std")]
587    pub receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>>,
588    #[cfg(not(feature = "std"))]
589    pub receiver: *const core::ffi::c_void,
590
591    #[cfg(feature = "std")]
592    pub dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>>,
593    #[cfg(not(feature = "std"))]
594    pub dropcheck: *const core::ffi::c_void,
595
596    pub writeback_data: RefAny,
597    pub check_thread_finished_fn: CheckThreadFinishedCallback,
598    pub send_thread_msg_fn: LibrarySendThreadMsgCallback,
599    pub receive_thread_msg_fn: LibraryReceiveThreadMsgCallback,
600    pub thread_destructor_fn: ThreadDestructorCallback,
601}
602
603#[cfg(feature = "std")]
604impl ThreadInner {
605    /// Returns true if the Thread has been finished, false otherwise
606    pub fn is_finished(&self) -> bool {
607        (self.check_thread_finished_fn.cb)(
608            self.dropcheck.as_ref() as *const _ as *const core::ffi::c_void
609        )
610    }
611
612    /// Send a message to the thread
613    pub fn sender_send(&mut self, msg: ThreadSendMsg) -> bool {
614        (self.send_thread_msg_fn.cb)(
615            self.sender.as_ref() as *const _ as *const core::ffi::c_void,
616            msg,
617        )
618    }
619
620    /// Try to receive a message from the thread (non-blocking)
621    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
622        (self.receive_thread_msg_fn.cb)(
623            self.receiver.as_ref() as *const _ as *const core::ffi::c_void
624        )
625    }
626}
627
628#[cfg(not(feature = "std"))]
629impl ThreadInner {
630    /// Returns true if the Thread has been finished, false otherwise
631    pub fn is_finished(&self) -> bool {
632        true
633    }
634
635    /// Send a message to the thread (no-op in no_std)
636    pub fn sender_send(&mut self, _msg: ThreadSendMsg) -> bool {
637        false
638    }
639
640    /// Try to receive a message from the thread (always returns None in no_std)
641    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
642        None.into()
643    }
644}
645
646impl Drop for ThreadInner {
647    fn drop(&mut self) {
648        (self.thread_destructor_fn.cb)(self);
649    }
650}
651
652// Default callback implementations for std
653#[cfg(feature = "std")]
654extern "C" fn default_thread_destructor_fn(thread: *mut ThreadInner) {
655    let thread = unsafe { &mut *thread };
656
657    if let Some(thread_handle) = thread.thread_handle.take() {
658        let _ = thread.sender.send(ThreadSendMsg::TerminateThread);
659        let _ = thread_handle.join(); // ignore the result, don't panic
660    }
661}
662
663#[cfg(not(feature = "std"))]
664extern "C" fn default_thread_destructor_fn(_thread: *mut ThreadInner) {}
665
666#[cfg(feature = "std")]
667extern "C" fn library_send_thread_msg_fn(
668    sender: *const core::ffi::c_void,
669    msg: ThreadSendMsg,
670) -> bool {
671    unsafe { &*(sender as *const Sender<ThreadSendMsg>) }
672        .send(msg)
673        .is_ok()
674}
675
676#[cfg(not(feature = "std"))]
677extern "C" fn library_send_thread_msg_fn(
678    _sender: *const core::ffi::c_void,
679    _msg: ThreadSendMsg,
680) -> bool {
681    false
682}
683
684#[cfg(feature = "std")]
685extern "C" fn library_receive_thread_msg_fn(
686    receiver: *const core::ffi::c_void,
687) -> OptionThreadReceiveMsg {
688    unsafe { &*(receiver as *const Receiver<ThreadReceiveMsg>) }
689        .try_recv()
690        .ok()
691        .into()
692}
693
694#[cfg(not(feature = "std"))]
695extern "C" fn library_receive_thread_msg_fn(
696    _receiver: *const core::ffi::c_void,
697) -> OptionThreadReceiveMsg {
698    None.into()
699}
700
701#[cfg(feature = "std")]
702extern "C" fn default_send_thread_msg_fn(
703    sender: *const core::ffi::c_void,
704    msg: ThreadReceiveMsg,
705) -> bool {
706    unsafe { &*(sender as *const Sender<ThreadReceiveMsg>) }
707        .send(msg)
708        .is_ok()
709}
710
711#[cfg(not(feature = "std"))]
712extern "C" fn default_send_thread_msg_fn(
713    _sender: *const core::ffi::c_void,
714    _msg: ThreadReceiveMsg,
715) -> bool {
716    false
717}
718
719#[cfg(feature = "std")]
720extern "C" fn default_receive_thread_msg_fn(
721    receiver: *const core::ffi::c_void,
722) -> OptionThreadSendMsg {
723    unsafe { &*(receiver as *const Receiver<ThreadSendMsg>) }
724        .try_recv()
725        .ok()
726        .into()
727}
728
729#[cfg(not(feature = "std"))]
730extern "C" fn default_receive_thread_msg_fn(
731    _receiver: *const core::ffi::c_void,
732) -> OptionThreadSendMsg {
733    None.into()
734}
735
736#[cfg(feature = "std")]
737extern "C" fn default_check_thread_finished(dropcheck: *const core::ffi::c_void) -> bool {
738    let weak = unsafe { &*(dropcheck as *const alloc::sync::Weak<()>) };
739    weak.upgrade().is_none()
740}
741
742#[cfg(not(feature = "std"))]
743extern "C" fn default_check_thread_finished(_dropcheck: *const core::ffi::c_void) -> bool {
744    true
745}
746
747#[cfg(feature = "std")]
748extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
749
750#[cfg(not(feature = "std"))]
751extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
752
753#[cfg(feature = "std")]
754extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
755
756#[cfg(not(feature = "std"))]
757extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
758
759/// Function that creates a new Thread object
760pub type CreateThreadCallbackType = extern "C" fn(RefAny, RefAny, ThreadCallback) -> Thread;
761
762#[repr(C)]
763pub struct CreateThreadCallback {
764    pub cb: CreateThreadCallbackType,
765}
766
767impl core::fmt::Debug for CreateThreadCallback {
768    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
769        write!(
770            f,
771            "CreateThreadCallback {{ cb: {:p} }}",
772            self.cb as *const ()
773        )
774    }
775}
776
777impl Clone for CreateThreadCallback {
778    fn clone(&self) -> Self {
779        Self { cb: self.cb }
780    }
781}
782
783impl Copy for CreateThreadCallback {}
784
785impl PartialEq for CreateThreadCallback {
786    fn eq(&self, other: &Self) -> bool {
787        self.cb as usize == other.cb as usize
788    }
789}
790
791impl Eq for CreateThreadCallback {}
792
793impl PartialOrd for CreateThreadCallback {
794    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
795        (self.cb as usize).partial_cmp(&(other.cb as usize))
796    }
797}
798
799impl Ord for CreateThreadCallback {
800    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
801        (self.cb as usize).cmp(&(other.cb as usize))
802    }
803}
804
805impl core::hash::Hash for CreateThreadCallback {
806    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
807        (self.cb as usize).hash(state);
808    }
809}
810
811/// Create a new thread using the standard library
812#[cfg(feature = "std")]
813pub extern "C" fn create_thread_libstd(
814    thread_initialize_data: RefAny,
815    writeback_data: RefAny,
816    callback: ThreadCallback,
817) -> Thread {
818    let (sender_receiver, receiver_receiver) = channel::<ThreadReceiveMsg>();
819    let mut sender_receiver = ThreadSender::new(ThreadSenderInner {
820        ptr: alloc::boxed::Box::new(sender_receiver),
821        send_fn: ThreadSendCallback {
822            cb: default_send_thread_msg_fn,
823        },
824        destructor: ThreadSenderDestructorCallback {
825            cb: thread_sender_drop,
826        },
827    });
828    // Set the ctx from the callback for FFI
829    sender_receiver.ctx = callback.ctx.clone();
830
831    let (sender_sender, receiver_sender) = channel::<ThreadSendMsg>();
832    let mut receiver_sender = ThreadReceiver::new(ThreadReceiverInner {
833        ptr: alloc::boxed::Box::new(receiver_sender),
834        recv_fn: ThreadRecvCallback {
835            cb: default_receive_thread_msg_fn,
836        },
837        destructor: ThreadReceiverDestructorCallback {
838            cb: thread_receiver_drop,
839        },
840    });
841    // Set the ctx from the callback for FFI
842    receiver_sender.ctx = callback.ctx.clone();
843
844    let thread_check = Arc::new(());
845    let dropcheck = Arc::downgrade(&thread_check);
846
847    let thread_handle = Some(thread::spawn(move || {
848        // Keep thread_check alive for the entire duration of the thread
849        // by binding it to a named variable (not `_` which drops immediately)
850        let _thread_check_guard = thread_check;
851        (callback.cb)(thread_initialize_data, sender_receiver, receiver_sender);
852        // _thread_check_guard gets dropped here, signals that the thread has finished
853    }));
854
855    let thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>> =
856        alloc::boxed::Box::new(thread_handle);
857    let sender: alloc::boxed::Box<Sender<ThreadSendMsg>> = alloc::boxed::Box::new(sender_sender);
858    let receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>> =
859        alloc::boxed::Box::new(receiver_receiver);
860    let dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>> = alloc::boxed::Box::new(dropcheck);
861
862    Thread::new(ThreadInner {
863        thread_handle,
864        sender,
865        receiver,
866        writeback_data,
867        dropcheck,
868        thread_destructor_fn: ThreadDestructorCallback {
869            cb: default_thread_destructor_fn,
870        },
871        check_thread_finished_fn: CheckThreadFinishedCallback {
872            cb: default_check_thread_finished,
873        },
874        send_thread_msg_fn: LibrarySendThreadMsgCallback {
875            cb: library_send_thread_msg_fn,
876        },
877        receive_thread_msg_fn: LibraryReceiveThreadMsgCallback {
878            cb: library_receive_thread_msg_fn,
879        },
880    })
881}
882
883#[cfg(not(feature = "std"))]
884pub extern "C" fn create_thread_libstd(
885    _thread_initialize_data: RefAny,
886    _writeback_data: RefAny,
887    _callback: ThreadCallback,
888) -> Thread {
889    Thread {
890        ptr: core::ptr::null(),
891        run_destructor: false,
892    }
893}
894
895#[cfg(test)]
896mod tests {
897    use super::*;
898
899    extern "C" fn test_writeback_callback(
900        _thread_data: RefAny,
901        _writeback_data: RefAny,
902        _callback_info: CallbackInfo,
903    ) -> Update {
904        Update::DoNothing
905    }
906
907    #[test]
908    fn test_writeback_callback_creation() {
909        let callback = WriteBackCallback::new(test_writeback_callback);
910        assert_eq!(callback.cb as usize, test_writeback_callback as usize);
911    }
912
913    #[test]
914    fn test_writeback_callback_clone() {
915        let callback = WriteBackCallback::new(test_writeback_callback);
916        let cloned = callback.clone();
917        assert_eq!(callback, cloned);
918    }
919}
920
921/// Optional Thread type for API compatibility
922#[derive(Debug, Clone)]
923#[repr(C, u8)]
924pub enum OptionThread {
925    None,
926    Some(Thread),
927}
928
929impl From<Option<Thread>> for OptionThread {
930    fn from(o: Option<Thread>) -> Self {
931        match o {
932            None => OptionThread::None,
933            Some(t) => OptionThread::Some(t),
934        }
935    }
936}
937
938impl OptionThread {
939    pub fn into_option(self) -> Option<Thread> {
940        match self {
941            OptionThread::None => None,
942            OptionThread::Some(t) => Some(t),
943        }
944    }
945}
946
947// ============================================================================
948// Sleep utilities
949// ============================================================================
950
951/// Sleeps the current thread for the specified number of milliseconds.
952///
953/// This is a cross-platform utility that can be called from C/C++/Python.
954///
955/// # Arguments
956/// * `milliseconds` - Number of milliseconds to sleep
957#[cfg(feature = "std")]
958pub fn thread_sleep_ms(milliseconds: u64) -> azul_css::corety::Void {
959    std::thread::sleep(std::time::Duration::from_millis(milliseconds));
960    azul_css::corety::Void::new()
961}
962
963/// Sleeps the current thread for the specified number of milliseconds (no-op on no_std).
964#[cfg(not(feature = "std"))]
965pub fn thread_sleep_ms(_milliseconds: u64) -> azul_css::corety::Void {
966    // No-op on no_std - can't sleep without OS
967    azul_css::corety::Void::new()
968}
969
970/// Sleeps the current thread for the specified number of microseconds.
971///
972/// # Arguments
973/// * `microseconds` - Number of microseconds to sleep
974#[cfg(feature = "std")]
975pub fn thread_sleep_us(microseconds: u64) -> azul_css::corety::Void {
976    std::thread::sleep(std::time::Duration::from_micros(microseconds));
977    azul_css::corety::Void::new()
978}
979
980/// Sleeps the current thread for the specified number of microseconds (no-op on no_std).
981#[cfg(not(feature = "std"))]
982pub fn thread_sleep_us(_microseconds: u64) -> azul_css::corety::Void {
983    // No-op on no_std - can't sleep without OS
984    azul_css::corety::Void::new()
985}
986
987/// Sleeps the current thread for the specified number of nanoseconds.
988///
989/// # Arguments
990/// * `nanoseconds` - Number of nanoseconds to sleep
991#[cfg(feature = "std")]
992pub fn thread_sleep_ns(nanoseconds: u64) -> azul_css::corety::Void {
993    std::thread::sleep(std::time::Duration::from_nanos(nanoseconds));
994    azul_css::corety::Void::new()
995}
996
997/// Sleeps the current thread for the specified number of nanoseconds (no-op on no_std).
998#[cfg(not(feature = "std"))]
999pub fn thread_sleep_ns(_nanoseconds: u64) -> azul_css::corety::Void {
1000    // No-op on no_std - can't sleep without OS
1001    azul_css::corety::Void::new()
1002}