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")]
7
use alloc::sync::Arc;
8
#[cfg(feature = "std")]
9
use std::sync::{
10
    mpsc::{channel, Receiver, Sender},
11
    Mutex,
12
};
13
#[cfg(feature = "std")]
14
use std::thread::{self, JoinHandle};
15

            
16
use azul_core::{
17
    callbacks::Update,
18
    refany::{OptionRefAny, RefAny},
19
    task::{
20
        CheckThreadFinishedCallback, CheckThreadFinishedCallbackType, LibrarySendThreadMsgCallback,
21
        LibrarySendThreadMsgCallbackType, OptionThreadSendMsg, ThreadId, ThreadReceiver,
22
        ThreadReceiverDestructorCallback, ThreadReceiverInner, ThreadRecvCallback, ThreadSendMsg,
23
    },
24
};
25

            
26
use crate::callbacks::CallbackInfo;
27

            
28
// Types that need to be defined locally (not in azul-core)
29

            
30
/// Message that is sent back from the running thread to the main thread
31
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
32
#[repr(C, u8)]
33
pub enum ThreadReceiveMsg {
34
    WriteBack(ThreadWriteBackMsg),
35
    Update(Update),
36
}
37

            
38
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
39
#[repr(C, u8)]
40
pub enum OptionThreadReceiveMsg {
41
    None,
42
    Some(ThreadReceiveMsg),
43
}
44

            
45
impl From<Option<ThreadReceiveMsg>> for OptionThreadReceiveMsg {
46
    fn from(inner: Option<ThreadReceiveMsg>) -> Self {
47
        match inner {
48
            None => OptionThreadReceiveMsg::None,
49
            Some(v) => OptionThreadReceiveMsg::Some(v),
50
        }
51
    }
52
}
53

            
54
impl OptionThreadReceiveMsg {
55
    pub fn into_option(self) -> Option<ThreadReceiveMsg> {
56
        match self {
57
            OptionThreadReceiveMsg::None => None,
58
            OptionThreadReceiveMsg::Some(v) => Some(v),
59
        }
60
    }
61

            
62
    pub fn as_ref(&self) -> Option<&ThreadReceiveMsg> {
63
        match self {
64
            OptionThreadReceiveMsg::None => None,
65
            OptionThreadReceiveMsg::Some(v) => Some(v),
66
        }
67
    }
68
}
69

            
70
/// Message containing writeback data and callback
71
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
72
#[repr(C)]
73
pub struct ThreadWriteBackMsg {
74
    pub refany: RefAny,
75
    pub callback: WriteBackCallback,
76
}
77

            
78
impl ThreadWriteBackMsg {
79
    pub fn new<C: Into<WriteBackCallback>>(callback: C, data: RefAny) -> Self {
80
        Self {
81
            refany: data,
82
            callback: callback.into(),
83
        }
84
    }
85
}
86

            
87
/// ThreadSender allows sending messages from the background thread to the main thread
88
#[derive(Debug)]
89
#[repr(C)]
90
pub struct ThreadSender {
91
    #[cfg(feature = "std")]
92
    pub ptr: alloc::boxed::Box<Arc<Mutex<ThreadSenderInner>>>,
93
    #[cfg(not(feature = "std"))]
94
    pub ptr: *const core::ffi::c_void,
95
    pub run_destructor: bool,
96
    /// For FFI: stores the foreign callable (e.g., PyFunction)
97
    pub ctx: OptionRefAny,
98
}
99

            
100
impl Clone for ThreadSender {
101
    fn clone(&self) -> Self {
102
        Self {
103
            ptr: self.ptr.clone(),
104
            run_destructor: true,
105
            ctx: self.ctx.clone(),
106
        }
107
    }
108
}
109

            
110
impl Drop for ThreadSender {
111
    fn drop(&mut self) {
112
        self.run_destructor = false;
113
    }
114
}
115

            
116
impl ThreadSender {
117
    #[cfg(not(feature = "std"))]
118
    pub fn new(_t: ThreadSenderInner) -> Self {
119
        Self {
120
            ptr: core::ptr::null(),
121
            run_destructor: false,
122
            ctx: OptionRefAny::None,
123
        }
124
    }
125

            
126
    #[cfg(feature = "std")]
127
    pub fn new(t: ThreadSenderInner) -> Self {
128
        Self {
129
            ptr: alloc::boxed::Box::new(Arc::new(Mutex::new(t))),
130
            run_destructor: true,
131
            ctx: OptionRefAny::None,
132
        }
133
    }
134

            
135
    /// Get the FFI context (e.g., Python callable)
136
    pub fn get_ctx(&self) -> OptionRefAny {
137
        self.ctx.clone()
138
    }
139

            
140
    #[cfg(not(feature = "std"))]
141
    pub fn send(&mut self, _msg: ThreadReceiveMsg) -> bool {
142
        false
143
    }
144

            
145
    #[cfg(feature = "std")]
146
    pub fn send(&mut self, msg: ThreadReceiveMsg) -> bool {
147
        let ts = match self.ptr.lock().ok() {
148
            Some(s) => s,
149
            None => return false,
150
        };
151
        (ts.send_fn.cb)(ts.ptr.as_ref() as *const _ as *const core::ffi::c_void, msg)
152
    }
153
}
154

            
155
/// Inner state of a `ThreadSender`, holding the channel sender and associated callbacks
156
#[derive(Debug)]
157
#[cfg_attr(not(feature = "std"), derive(PartialEq, PartialOrd, Eq, Ord))]
158
#[repr(C)]
159
pub struct ThreadSenderInner {
160
    #[cfg(feature = "std")]
161
    pub ptr: alloc::boxed::Box<Sender<ThreadReceiveMsg>>,
162
    #[cfg(not(feature = "std"))]
163
    pub ptr: *const core::ffi::c_void,
164
    pub send_fn: ThreadSendCallback,
165
    pub destructor: ThreadSenderDestructorCallback,
166
}
167

            
168
#[cfg(not(feature = "std"))]
169
unsafe impl Send for ThreadSenderInner {}
170

            
171
#[cfg(feature = "std")]
172
impl core::hash::Hash for ThreadSenderInner {
173
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
174
        (self.ptr.as_ref() as *const _ as usize).hash(state);
175
    }
176
}
177

            
178
#[cfg(feature = "std")]
179
impl PartialEq for ThreadSenderInner {
180
    fn eq(&self, other: &Self) -> bool {
181
        (self.ptr.as_ref() as *const _ as usize) == (other.ptr.as_ref() as *const _ as usize)
182
    }
183
}
184

            
185
#[cfg(feature = "std")]
186
impl Eq for ThreadSenderInner {}
187

            
188
#[cfg(feature = "std")]
189
impl PartialOrd for ThreadSenderInner {
190
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
191
        Some(
192
            (self.ptr.as_ref() as *const _ as usize)
193
                .cmp(&(other.ptr.as_ref() as *const _ as usize)),
194
        )
195
    }
196
}
197

            
198
#[cfg(feature = "std")]
199
impl Ord for ThreadSenderInner {
200
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
201
        (self.ptr.as_ref() as *const _ as usize).cmp(&(other.ptr.as_ref() as *const _ as usize))
202
    }
203
}
204

            
205
impl Drop for ThreadSenderInner {
206
    fn drop(&mut self) {
207
        (self.destructor.cb)(self);
208
    }
209
}
210

            
211
/// Callback for sending messages from thread to main thread
212
pub type ThreadSendCallbackType = extern "C" fn(*const core::ffi::c_void, ThreadReceiveMsg) -> bool;
213

            
214
#[repr(C)]
215
pub struct ThreadSendCallback {
216
    pub cb: ThreadSendCallbackType,
217
}
218

            
219
impl core::fmt::Debug for ThreadSendCallback {
220
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
221
        write!(f, "ThreadSendCallback {{ cb: {:p} }}", self.cb as *const ())
222
    }
223
}
224

            
225
impl Clone for ThreadSendCallback {
226
    fn clone(&self) -> Self {
227
        Self { cb: self.cb }
228
    }
229
}
230

            
231
impl PartialEq for ThreadSendCallback {
232
    fn eq(&self, other: &Self) -> bool {
233
        self.cb as usize == other.cb as usize
234
    }
235
}
236

            
237
impl Eq for ThreadSendCallback {}
238

            
239
impl PartialOrd for ThreadSendCallback {
240
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
241
        Some(self.cmp(other))
242
    }
243
}
244

            
245
impl Ord for ThreadSendCallback {
246
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
247
        (self.cb as usize).cmp(&(other.cb as usize))
248
    }
249
}
250

            
251
/// Destructor callback for ThreadSender
252
pub type ThreadSenderDestructorCallbackType = extern "C" fn(*mut ThreadSenderInner);
253

            
254
#[repr(C)]
255
pub struct ThreadSenderDestructorCallback {
256
    pub cb: ThreadSenderDestructorCallbackType,
257
}
258

            
259
impl core::fmt::Debug for ThreadSenderDestructorCallback {
260
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
261
        write!(
262
            f,
263
            "ThreadSenderDestructorCallback {{ cb: {:p} }}",
264
            self.cb as *const ()
265
        )
266
    }
267
}
268

            
269
impl Clone for ThreadSenderDestructorCallback {
270
    fn clone(&self) -> Self {
271
        Self { cb: self.cb }
272
    }
273
}
274

            
275
impl PartialEq for ThreadSenderDestructorCallback {
276
    fn eq(&self, other: &Self) -> bool {
277
        self.cb as usize == other.cb as usize
278
    }
279
}
280

            
281
impl Eq for ThreadSenderDestructorCallback {}
282

            
283
impl PartialOrd for ThreadSenderDestructorCallback {
284
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
285
        Some(self.cmp(other))
286
    }
287
}
288

            
289
impl Ord for ThreadSenderDestructorCallback {
290
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
291
        (self.cb as usize).cmp(&(other.cb as usize))
292
    }
293
}
294

            
295
/// Callback that runs when a thread receives a `WriteBack` message
296
///
297
/// This callback runs on the main UI thread and has access to:
298
/// - The thread's original data
299
/// - Data sent back from the background thread
300
/// - Full CallbackInfo for DOM queries and UI updates
301
pub type WriteBackCallbackType = extern "C" fn(
302
    /* original thread data */ RefAny,
303
    /* data to write back */ RefAny,
304
    /* callback info */ CallbackInfo,
305
) -> Update;
306

            
307
/// Callback that can run when a thread receives a `WriteBack` message
308
#[repr(C)]
309
pub struct WriteBackCallback {
310
    pub cb: WriteBackCallbackType,
311
    /// For FFI: stores the foreign callable (e.g., PyFunction)
312
    /// Native Rust code sets this to None
313
    pub ctx: OptionRefAny,
314
}
315

            
316
impl WriteBackCallback {
317
    /// Create a new WriteBackCallback
318
2
    pub fn new(cb: WriteBackCallbackType) -> Self {
319
2
        Self {
320
2
            cb,
321
2
            ctx: OptionRefAny::None,
322
2
        }
323
2
    }
324

            
325
    /// Invoke the callback
326
    pub fn invoke(
327
        &self,
328
        thread_data: RefAny,
329
        writeback_data: RefAny,
330
        callback_info: CallbackInfo,
331
    ) -> Update {
332
        (self.cb)(thread_data, writeback_data, callback_info)
333
    }
334
}
335

            
336
impl core::fmt::Debug for WriteBackCallback {
337
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
338
        write!(f, "WriteBackCallback {{ cb: {:p} }}", self.cb as *const ())
339
    }
340
}
341

            
342
impl Clone for WriteBackCallback {
343
1
    fn clone(&self) -> Self {
344
1
        Self {
345
1
            cb: self.cb,
346
1
            ctx: self.ctx.clone(),
347
1
        }
348
1
    }
349
}
350

            
351
impl From<WriteBackCallbackType> for WriteBackCallback {
352
    fn from(cb: WriteBackCallbackType) -> Self {
353
        Self {
354
            cb,
355
            ctx: OptionRefAny::None,
356
        }
357
    }
358
}
359

            
360
impl PartialEq for WriteBackCallback {
361
1
    fn eq(&self, other: &Self) -> bool {
362
1
        self.cb as usize == other.cb as usize
363
1
    }
364
}
365

            
366
impl Eq for WriteBackCallback {}
367

            
368
impl PartialOrd for WriteBackCallback {
369
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
370
        (self.cb as usize).partial_cmp(&(other.cb as usize))
371
    }
372
}
373

            
374
impl Ord for WriteBackCallback {
375
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
376
        (self.cb as usize).cmp(&(other.cb as usize))
377
    }
378
}
379

            
380
impl core::hash::Hash for WriteBackCallback {
381
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
382
        (self.cb as usize).hash(state);
383
    }
384
}
385

            
386
/// Callback type for the function that runs in the background thread
387
pub type ThreadCallbackType = extern "C" fn(RefAny, ThreadSender, ThreadReceiver);
388

            
389
#[repr(C)]
390
pub struct ThreadCallback {
391
    pub cb: ThreadCallbackType,
392
    /// For FFI: stores the foreign callable (e.g., PyFunction)
393
    /// Native Rust code sets this to None
394
    pub ctx: OptionRefAny,
395
}
396

            
397
impl ThreadCallback {
398
    /// Create a new ThreadCallback
399
    pub fn new(cb: ThreadCallbackType) -> Self {
400
        Self {
401
            cb,
402
            ctx: OptionRefAny::None,
403
        }
404
    }
405
}
406

            
407
impl core::fmt::Debug for ThreadCallback {
408
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
409
        write!(f, "ThreadCallback {{ cb: {:p} }}", self.cb as *const ())
410
    }
411
}
412

            
413
impl Clone for ThreadCallback {
414
    fn clone(&self) -> Self {
415
        Self {
416
            cb: self.cb,
417
            ctx: self.ctx.clone(),
418
        }
419
    }
420
}
421

            
422
impl From<ThreadCallbackType> for ThreadCallback {
423
    fn from(cb: ThreadCallbackType) -> Self {
424
        Self {
425
            cb,
426
            ctx: OptionRefAny::None,
427
        }
428
    }
429
}
430

            
431
impl PartialEq for ThreadCallback {
432
    fn eq(&self, other: &Self) -> bool {
433
        self.cb as usize == other.cb as usize
434
    }
435
}
436

            
437
impl Eq for ThreadCallback {}
438

            
439
impl PartialOrd for ThreadCallback {
440
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
441
        (self.cb as usize).partial_cmp(&(other.cb as usize))
442
    }
443
}
444

            
445
impl Ord for ThreadCallback {
446
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
447
        (self.cb as usize).cmp(&(other.cb as usize))
448
    }
449
}
450

            
451
impl core::hash::Hash for ThreadCallback {
452
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
453
        (self.cb as usize).hash(state);
454
    }
455
}
456

            
457
// Host-invoker plumbing for ThreadCallback. NOTE: this callback fires
458
// on a worker thread (spawned by `Thread::create`), not the main
459
// `App.run` thread. The per-language host-invoker thunk MUST acquire
460
// the host VM lock before dispatching to user code:
461
//   * CPython: PyGILState_Ensure / _Release
462
//   * MRI Ruby: rb_thread_call_with_gvl
463
//   * OpenJDK: AttachCurrentThread / DetachCurrentThread
464
//   * CLR / .NET: nothing ([UnmanagedCallersOnly] auto-trampolines)
465
//   * OCaml: caml_acquire_runtime_system / _release
466
//   * Lua / Perl / PHP / Pharo: cannot be called from worker thread
467
//     (single-threaded interpreter) — fall back to writeback-only
468
//     pattern (Rust extern "C" cb on worker, host fn on main via
469
//     WriteBackCallback).
470
// See `scripts/BINDING_STRATEGY_PER_LANGUAGE.md` for the lock-acquire
471
// table per VM.
472
azul_core::impl_managed_callback! {
473
    wrapper:        ThreadCallback,
474
    info_ty:        ThreadSender,
475
    return_ty:      (),
476
    default_ret:    (),
477
    invoker_static: THREAD_CALLBACK_INVOKER,
478
    invoker_ty:     AzThreadCallbackInvoker,
479
    thunk_fn:       az_thread_callback_thunk,
480
    setter_fn:      AzApp_setThreadCallbackInvoker,
481
    from_handle_fn: AzThreadCallback_createFromHostHandle,
482
    extra_args:     [receiver: ThreadReceiver],
483
}
484

            
485
/// Callback type for receiving messages from a background thread
486
pub type LibraryReceiveThreadMsgCallbackType =
487
    extern "C" fn(*const core::ffi::c_void) -> OptionThreadReceiveMsg;
488

            
489
#[repr(C)]
490
pub struct LibraryReceiveThreadMsgCallback {
491
    pub cb: LibraryReceiveThreadMsgCallbackType,
492
}
493

            
494
impl core::fmt::Debug for LibraryReceiveThreadMsgCallback {
495
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
496
        write!(
497
            f,
498
            "LibraryReceiveThreadMsgCallback {{ cb: {:p} }}",
499
            self.cb as *const ()
500
        )
501
    }
502
}
503

            
504
impl Clone for LibraryReceiveThreadMsgCallback {
505
    fn clone(&self) -> Self {
506
        Self { cb: self.cb }
507
    }
508
}
509

            
510
/// Callback type for the destructor that cleans up a `ThreadInner`
511
pub type ThreadDestructorCallbackType = extern "C" fn(*mut ThreadInner);
512

            
513
#[repr(C)]
514
pub struct ThreadDestructorCallback {
515
    pub cb: ThreadDestructorCallbackType,
516
}
517

            
518
impl core::fmt::Debug for ThreadDestructorCallback {
519
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
520
        write!(
521
            f,
522
            "ThreadDestructorCallback {{ cb: {:p} }}",
523
            self.cb as *const ()
524
        )
525
    }
526
}
527

            
528
impl Clone for ThreadDestructorCallback {
529
    fn clone(&self) -> Self {
530
        Self { cb: self.cb }
531
    }
532
}
533

            
534
/// Wrapper around Thread because Thread needs to be clone-able
535
#[derive(Debug)]
536
#[repr(C)]
537
pub struct Thread {
538
    #[cfg(feature = "std")]
539
    pub ptr: alloc::boxed::Box<Arc<Mutex<ThreadInner>>>,
540
    #[cfg(not(feature = "std"))]
541
    pub ptr: *const core::ffi::c_void,
542
    pub run_destructor: bool,
543
}
544

            
545
impl Clone for Thread {
546
    fn clone(&self) -> Self {
547
        Self {
548
            ptr: self.ptr.clone(),
549
            run_destructor: true,
550
        }
551
    }
552
}
553

            
554
impl Drop for Thread {
555
    fn drop(&mut self) {
556
        self.run_destructor = false;
557
    }
558
}
559

            
560
impl Thread {
561
    #[cfg(feature = "std")]
562
    pub fn new(ti: ThreadInner) -> Self {
563
        Self {
564
            ptr: alloc::boxed::Box::new(Arc::new(Mutex::new(ti))),
565
            run_destructor: true,
566
        }
567
    }
568

            
569
    #[cfg(not(feature = "std"))]
570
    pub fn new(_ti: ThreadInner) -> Self {
571
        Self {
572
            ptr: core::ptr::null(),
573
            run_destructor: false,
574
        }
575
    }
576

            
577
    /// Creates a new thread that will execute the given callback function.
578
    ///
579
    /// # Arguments
580
    /// * `thread_initialize_data` - Data passed to the callback when the thread starts
581
    /// * `writeback_data` - Data that will be passed back when writeback messages are received
582
    /// * `callback` - The callback to execute in the background thread
583
    ///
584
    /// # Returns
585
    /// A new Thread handle that can be added to the event loop with `CallbackInfo::add_thread`
586
    pub fn create<C: Into<ThreadCallback>>(
587
        thread_initialize_data: RefAny,
588
        writeback_data: RefAny,
589
        callback: C,
590
    ) -> Self {
591
        create_thread_libstd(thread_initialize_data, writeback_data, callback.into())
592
    }
593
}
594

            
595
/// A `Thread` is a separate thread that is owned by the framework.
596
///
597
/// In difference to a regular thread, you don't have to `await()` the result,
598
/// you can just hand the Thread to the framework and it will automatically
599
/// update the UI when the Thread is finished.
600
#[derive(Debug)]
601
#[repr(C)]
602
pub struct ThreadInner {
603
    #[cfg(feature = "std")]
604
    pub thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>>,
605
    #[cfg(not(feature = "std"))]
606
    pub thread_handle: *const core::ffi::c_void,
607

            
608
    #[cfg(feature = "std")]
609
    pub sender: alloc::boxed::Box<Sender<ThreadSendMsg>>,
610
    #[cfg(not(feature = "std"))]
611
    pub sender: *const core::ffi::c_void,
612

            
613
    #[cfg(feature = "std")]
614
    pub receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>>,
615
    #[cfg(not(feature = "std"))]
616
    pub receiver: *const core::ffi::c_void,
617

            
618
    #[cfg(feature = "std")]
619
    pub dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>>,
620
    #[cfg(not(feature = "std"))]
621
    pub dropcheck: *const core::ffi::c_void,
622

            
623
    pub writeback_data: RefAny,
624
    pub check_thread_finished_fn: CheckThreadFinishedCallback,
625
    pub send_thread_msg_fn: LibrarySendThreadMsgCallback,
626
    pub receive_thread_msg_fn: LibraryReceiveThreadMsgCallback,
627
    pub thread_destructor_fn: ThreadDestructorCallback,
628
}
629

            
630
#[cfg(feature = "std")]
631
impl ThreadInner {
632
    /// Returns true if the Thread has been finished, false otherwise
633
    pub fn is_finished(&self) -> bool {
634
        (self.check_thread_finished_fn.cb)(
635
            self.dropcheck.as_ref() as *const _ as *const core::ffi::c_void
636
        )
637
    }
638

            
639
    /// Send a message to the thread
640
    pub fn sender_send(&mut self, msg: ThreadSendMsg) -> bool {
641
        (self.send_thread_msg_fn.cb)(
642
            self.sender.as_ref() as *const _ as *const core::ffi::c_void,
643
            msg,
644
        )
645
    }
646

            
647
    /// Try to receive a message from the thread (non-blocking)
648
    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
649
        (self.receive_thread_msg_fn.cb)(
650
            self.receiver.as_ref() as *const _ as *const core::ffi::c_void
651
        )
652
    }
653
}
654

            
655
#[cfg(not(feature = "std"))]
656
impl ThreadInner {
657
    /// Returns true if the Thread has been finished, false otherwise
658
    pub fn is_finished(&self) -> bool {
659
        true
660
    }
661

            
662
    /// Send a message to the thread (no-op in no_std)
663
    pub fn sender_send(&mut self, _msg: ThreadSendMsg) -> bool {
664
        false
665
    }
666

            
667
    /// Try to receive a message from the thread (always returns None in no_std)
668
    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
669
        None.into()
670
    }
671
}
672

            
673
impl Drop for ThreadInner {
674
    fn drop(&mut self) {
675
        (self.thread_destructor_fn.cb)(self);
676
    }
677
}
678

            
679
// Default callback implementations for std
680
#[cfg(feature = "std")]
681
extern "C" fn default_thread_destructor_fn(thread: *mut ThreadInner) {
682
    let thread = unsafe { &mut *thread };
683

            
684
    if let Some(thread_handle) = thread.thread_handle.take() {
685
        let _ = thread.sender.send(ThreadSendMsg::TerminateThread);
686
        let _ = thread_handle.join(); // ignore the result, don't panic
687
    }
688
}
689

            
690
#[cfg(not(feature = "std"))]
691
extern "C" fn default_thread_destructor_fn(_thread: *mut ThreadInner) {}
692

            
693
#[cfg(feature = "std")]
694
extern "C" fn library_send_thread_msg_fn(
695
    sender: *const core::ffi::c_void,
696
    msg: ThreadSendMsg,
697
) -> bool {
698
    unsafe { &*(sender as *const Sender<ThreadSendMsg>) }
699
        .send(msg)
700
        .is_ok()
701
}
702

            
703
#[cfg(not(feature = "std"))]
704
extern "C" fn library_send_thread_msg_fn(
705
    _sender: *const core::ffi::c_void,
706
    _msg: ThreadSendMsg,
707
) -> bool {
708
    false
709
}
710

            
711
#[cfg(feature = "std")]
712
extern "C" fn library_receive_thread_msg_fn(
713
    receiver: *const core::ffi::c_void,
714
) -> OptionThreadReceiveMsg {
715
    unsafe { &*(receiver as *const Receiver<ThreadReceiveMsg>) }
716
        .try_recv()
717
        .ok()
718
        .into()
719
}
720

            
721
#[cfg(not(feature = "std"))]
722
extern "C" fn library_receive_thread_msg_fn(
723
    _receiver: *const core::ffi::c_void,
724
) -> OptionThreadReceiveMsg {
725
    None.into()
726
}
727

            
728
#[cfg(feature = "std")]
729
extern "C" fn default_send_thread_msg_fn(
730
    sender: *const core::ffi::c_void,
731
    msg: ThreadReceiveMsg,
732
) -> bool {
733
    unsafe { &*(sender as *const Sender<ThreadReceiveMsg>) }
734
        .send(msg)
735
        .is_ok()
736
}
737

            
738
#[cfg(not(feature = "std"))]
739
extern "C" fn default_send_thread_msg_fn(
740
    _sender: *const core::ffi::c_void,
741
    _msg: ThreadReceiveMsg,
742
) -> bool {
743
    false
744
}
745

            
746
#[cfg(feature = "std")]
747
extern "C" fn default_receive_thread_msg_fn(
748
    receiver: *const core::ffi::c_void,
749
) -> OptionThreadSendMsg {
750
    unsafe { &*(receiver as *const Receiver<ThreadSendMsg>) }
751
        .try_recv()
752
        .ok()
753
        .into()
754
}
755

            
756
#[cfg(not(feature = "std"))]
757
extern "C" fn default_receive_thread_msg_fn(
758
    _receiver: *const core::ffi::c_void,
759
) -> OptionThreadSendMsg {
760
    None.into()
761
}
762

            
763
#[cfg(feature = "std")]
764
extern "C" fn default_check_thread_finished(dropcheck: *const core::ffi::c_void) -> bool {
765
    let weak = unsafe { &*(dropcheck as *const alloc::sync::Weak<()>) };
766
    weak.upgrade().is_none()
767
}
768

            
769
#[cfg(not(feature = "std"))]
770
extern "C" fn default_check_thread_finished(_dropcheck: *const core::ffi::c_void) -> bool {
771
    true
772
}
773

            
774
#[cfg(feature = "std")]
775
extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
776

            
777
#[cfg(not(feature = "std"))]
778
extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
779

            
780
#[cfg(feature = "std")]
781
extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
782

            
783
#[cfg(not(feature = "std"))]
784
extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
785

            
786
/// Function that creates a new Thread object
787
pub type CreateThreadCallbackType = extern "C" fn(RefAny, RefAny, ThreadCallback) -> Thread;
788

            
789
#[repr(C)]
790
pub struct CreateThreadCallback {
791
    pub cb: CreateThreadCallbackType,
792
}
793

            
794
impl core::fmt::Debug for CreateThreadCallback {
795
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
796
        write!(
797
            f,
798
            "CreateThreadCallback {{ cb: {:p} }}",
799
            self.cb as *const ()
800
        )
801
    }
802
}
803

            
804
impl Clone for CreateThreadCallback {
805
    fn clone(&self) -> Self {
806
        Self { cb: self.cb }
807
    }
808
}
809

            
810
impl Copy for CreateThreadCallback {}
811

            
812
impl PartialEq for CreateThreadCallback {
813
    fn eq(&self, other: &Self) -> bool {
814
        self.cb as usize == other.cb as usize
815
    }
816
}
817

            
818
impl Eq for CreateThreadCallback {}
819

            
820
impl PartialOrd for CreateThreadCallback {
821
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
822
        (self.cb as usize).partial_cmp(&(other.cb as usize))
823
    }
824
}
825

            
826
impl Ord for CreateThreadCallback {
827
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
828
        (self.cb as usize).cmp(&(other.cb as usize))
829
    }
830
}
831

            
832
impl core::hash::Hash for CreateThreadCallback {
833
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
834
        (self.cb as usize).hash(state);
835
    }
836
}
837

            
838
/// Create a new thread using the standard library
839
#[cfg(feature = "std")]
840
pub extern "C" fn create_thread_libstd(
841
    thread_initialize_data: RefAny,
842
    writeback_data: RefAny,
843
    callback: ThreadCallback,
844
) -> Thread {
845
    let (sender_receiver, receiver_receiver) = channel::<ThreadReceiveMsg>();
846
    let mut sender_receiver = ThreadSender::new(ThreadSenderInner {
847
        ptr: alloc::boxed::Box::new(sender_receiver),
848
        send_fn: ThreadSendCallback {
849
            cb: default_send_thread_msg_fn,
850
        },
851
        destructor: ThreadSenderDestructorCallback {
852
            cb: thread_sender_drop,
853
        },
854
    });
855
    // Set the ctx from the callback for FFI
856
    sender_receiver.ctx = callback.ctx.clone();
857

            
858
    let (sender_sender, receiver_sender) = channel::<ThreadSendMsg>();
859
    let mut receiver_sender = ThreadReceiver::new(ThreadReceiverInner {
860
        ptr: alloc::boxed::Box::new(receiver_sender),
861
        recv_fn: ThreadRecvCallback {
862
            cb: default_receive_thread_msg_fn,
863
        },
864
        destructor: ThreadReceiverDestructorCallback {
865
            cb: thread_receiver_drop,
866
        },
867
    });
868
    // Set the ctx from the callback for FFI
869
    receiver_sender.ctx = callback.ctx.clone();
870

            
871
    let thread_check = Arc::new(());
872
    let dropcheck = Arc::downgrade(&thread_check);
873

            
874
    let thread_handle = Some(thread::spawn(move || {
875
        // Keep thread_check alive for the entire duration of the thread
876
        // by binding it to a named variable (not `_` which drops immediately)
877
        let _thread_check_guard = thread_check;
878
        (callback.cb)(thread_initialize_data, sender_receiver, receiver_sender);
879
        // _thread_check_guard gets dropped here, signals that the thread has finished
880
    }));
881

            
882
    let thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>> =
883
        alloc::boxed::Box::new(thread_handle);
884
    let sender: alloc::boxed::Box<Sender<ThreadSendMsg>> = alloc::boxed::Box::new(sender_sender);
885
    let receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>> =
886
        alloc::boxed::Box::new(receiver_receiver);
887
    let dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>> = alloc::boxed::Box::new(dropcheck);
888

            
889
    Thread::new(ThreadInner {
890
        thread_handle,
891
        sender,
892
        receiver,
893
        writeback_data,
894
        dropcheck,
895
        thread_destructor_fn: ThreadDestructorCallback {
896
            cb: default_thread_destructor_fn,
897
        },
898
        check_thread_finished_fn: CheckThreadFinishedCallback {
899
            cb: default_check_thread_finished,
900
        },
901
        send_thread_msg_fn: LibrarySendThreadMsgCallback {
902
            cb: library_send_thread_msg_fn,
903
        },
904
        receive_thread_msg_fn: LibraryReceiveThreadMsgCallback {
905
            cb: library_receive_thread_msg_fn,
906
        },
907
    })
908
}
909

            
910
#[cfg(not(feature = "std"))]
911
pub extern "C" fn create_thread_libstd(
912
    _thread_initialize_data: RefAny,
913
    _writeback_data: RefAny,
914
    _callback: ThreadCallback,
915
) -> Thread {
916
    Thread {
917
        ptr: core::ptr::null(),
918
        run_destructor: false,
919
    }
920
}
921

            
922
#[cfg(test)]
923
mod tests {
924
    use super::*;
925

            
926
    extern "C" fn test_writeback_callback(
927
        _thread_data: RefAny,
928
        _writeback_data: RefAny,
929
        _callback_info: CallbackInfo,
930
    ) -> Update {
931
        Update::DoNothing
932
    }
933

            
934
    #[test]
935
1
    fn test_writeback_callback_creation() {
936
1
        let callback = WriteBackCallback::new(test_writeback_callback);
937
1
        assert_eq!(callback.cb as usize, test_writeback_callback as usize);
938
1
    }
939

            
940
    #[test]
941
1
    fn test_writeback_callback_clone() {
942
1
        let callback = WriteBackCallback::new(test_writeback_callback);
943
1
        let cloned = callback.clone();
944
1
        assert_eq!(callback, cloned);
945
1
    }
946
}
947

            
948
/// Optional Thread type for API compatibility
949
#[derive(Debug, Clone)]
950
#[repr(C, u8)]
951
pub enum OptionThread {
952
    None,
953
    Some(Thread),
954
}
955

            
956
impl From<Option<Thread>> for OptionThread {
957
    fn from(o: Option<Thread>) -> Self {
958
        match o {
959
            None => OptionThread::None,
960
            Some(t) => OptionThread::Some(t),
961
        }
962
    }
963
}
964

            
965
impl OptionThread {
966
    pub fn into_option(self) -> Option<Thread> {
967
        match self {
968
            OptionThread::None => None,
969
            OptionThread::Some(t) => Some(t),
970
        }
971
    }
972
}
973

            
974
// ============================================================================
975
// Sleep utilities
976
// ============================================================================
977

            
978
/// Sleeps the current thread for the specified number of milliseconds.
979
///
980
/// This is a cross-platform utility that can be called from C/C++/Python.
981
///
982
/// # Arguments
983
/// * `milliseconds` - Number of milliseconds to sleep
984
#[cfg(feature = "std")]
985
pub fn thread_sleep_ms(milliseconds: u64) -> azul_css::corety::EmptyStruct {
986
    std::thread::sleep(std::time::Duration::from_millis(milliseconds));
987
    azul_css::corety::EmptyStruct::new()
988
}
989

            
990
/// Sleeps the current thread for the specified number of milliseconds (no-op on no_std).
991
#[cfg(not(feature = "std"))]
992
pub fn thread_sleep_ms(_milliseconds: u64) -> azul_css::corety::EmptyStruct {
993
    // No-op on no_std - can't sleep without OS
994
    azul_css::corety::EmptyStruct::new()
995
}
996

            
997
/// Sleeps the current thread for the specified number of microseconds.
998
///
999
/// # Arguments
/// * `microseconds` - Number of microseconds to sleep
#[cfg(feature = "std")]
pub fn thread_sleep_us(microseconds: u64) -> azul_css::corety::EmptyStruct {
    std::thread::sleep(std::time::Duration::from_micros(microseconds));
    azul_css::corety::EmptyStruct::new()
}
/// Sleeps the current thread for the specified number of microseconds (no-op on no_std).
#[cfg(not(feature = "std"))]
pub fn thread_sleep_us(_microseconds: u64) -> azul_css::corety::EmptyStruct {
    // No-op on no_std - can't sleep without OS
    azul_css::corety::EmptyStruct::new()
}
/// Sleeps the current thread for the specified number of nanoseconds.
///
/// # Arguments
/// * `nanoseconds` - Number of nanoseconds to sleep
#[cfg(feature = "std")]
pub fn thread_sleep_ns(nanoseconds: u64) -> azul_css::corety::EmptyStruct {
    std::thread::sleep(std::time::Duration::from_nanos(nanoseconds));
    azul_css::corety::EmptyStruct::new()
}
/// Sleeps the current thread for the specified number of nanoseconds (no-op on no_std).
#[cfg(not(feature = "std"))]
pub fn thread_sleep_ns(_nanoseconds: u64) -> azul_css::corety::EmptyStruct {
    // No-op on no_std - can't sleep without OS
    azul_css::corety::EmptyStruct::new()
}