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
    /// Send a control message to the running worker. Interior-mutable (the worker
595
    /// state is behind a `Mutex`), so this takes `&self` and is callable from a
596
    /// callback that only holds `&Thread` via `CallbackInfo::get_thread`. Used to push
597
    /// resize / seek / source-change messages to a persistent worker. Returns false
598
    /// if the channel is closed (or always, on no_std).
599
    #[cfg(feature = "std")]
600
    pub fn send_message(&self, msg: ThreadSendMsg) -> bool {
601
        match self.ptr.lock() {
602
            Ok(inner) => inner.sender.send(msg).is_ok(),
603
            Err(_) => false,
604
        }
605
    }
606
    #[cfg(not(feature = "std"))]
607
    pub fn send_message(&self, _msg: ThreadSendMsg) -> bool {
608
        false
609
    }
610

            
611
    /// Clone the main→worker `Sender` so a holder without a `CallbackInfo` (e.g. a
612
    /// dataset-merge callback) can message the running worker later — used for the
613
    /// scrub/seek path, where the merge callback compares the old/new `VideoConfig`
614
    /// and pushes a seek to the worker. `None` on no_std.
615
    #[cfg(feature = "std")]
616
    pub fn clone_sender(&self) -> Option<Sender<ThreadSendMsg>> {
617
        self.ptr.lock().ok().map(|inner| (*inner.sender).clone())
618
    }
619
    #[cfg(not(feature = "std"))]
620
    pub fn clone_sender(&self) -> Option<Sender<ThreadSendMsg>> {
621
        None
622
    }
623
}
624

            
625
/// A `Thread` is a separate thread that is owned by the framework.
626
///
627
/// In difference to a regular thread, you don't have to `await()` the result,
628
/// you can just hand the Thread to the framework and it will automatically
629
/// update the UI when the Thread is finished.
630
#[derive(Debug)]
631
#[repr(C)]
632
pub struct ThreadInner {
633
    #[cfg(feature = "std")]
634
    pub thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>>,
635
    #[cfg(not(feature = "std"))]
636
    pub thread_handle: *const core::ffi::c_void,
637

            
638
    #[cfg(feature = "std")]
639
    pub sender: alloc::boxed::Box<Sender<ThreadSendMsg>>,
640
    #[cfg(not(feature = "std"))]
641
    pub sender: *const core::ffi::c_void,
642

            
643
    #[cfg(feature = "std")]
644
    pub receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>>,
645
    #[cfg(not(feature = "std"))]
646
    pub receiver: *const core::ffi::c_void,
647

            
648
    #[cfg(feature = "std")]
649
    pub dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>>,
650
    #[cfg(not(feature = "std"))]
651
    pub dropcheck: *const core::ffi::c_void,
652

            
653
    pub writeback_data: RefAny,
654
    pub check_thread_finished_fn: CheckThreadFinishedCallback,
655
    pub send_thread_msg_fn: LibrarySendThreadMsgCallback,
656
    pub receive_thread_msg_fn: LibraryReceiveThreadMsgCallback,
657
    pub thread_destructor_fn: ThreadDestructorCallback,
658
}
659

            
660
#[cfg(feature = "std")]
661
impl ThreadInner {
662
    /// Returns true if the Thread has been finished, false otherwise
663
    pub fn is_finished(&self) -> bool {
664
        (self.check_thread_finished_fn.cb)(
665
            self.dropcheck.as_ref() as *const _ as *const core::ffi::c_void
666
        )
667
    }
668

            
669
    /// Send a message to the thread
670
    pub fn sender_send(&mut self, msg: ThreadSendMsg) -> bool {
671
        (self.send_thread_msg_fn.cb)(
672
            self.sender.as_ref() as *const _ as *const core::ffi::c_void,
673
            msg,
674
        )
675
    }
676

            
677
    /// Try to receive a message from the thread (non-blocking)
678
    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
679
        (self.receive_thread_msg_fn.cb)(
680
            self.receiver.as_ref() as *const _ as *const core::ffi::c_void
681
        )
682
    }
683
}
684

            
685
#[cfg(not(feature = "std"))]
686
impl ThreadInner {
687
    /// Returns true if the Thread has been finished, false otherwise
688
    pub fn is_finished(&self) -> bool {
689
        true
690
    }
691

            
692
    /// Send a message to the thread (no-op in no_std)
693
    pub fn sender_send(&mut self, _msg: ThreadSendMsg) -> bool {
694
        false
695
    }
696

            
697
    /// Try to receive a message from the thread (always returns None in no_std)
698
    pub fn receiver_try_recv(&mut self) -> OptionThreadReceiveMsg {
699
        None.into()
700
    }
701
}
702

            
703
impl Drop for ThreadInner {
704
    fn drop(&mut self) {
705
        (self.thread_destructor_fn.cb)(self);
706
    }
707
}
708

            
709
// Default callback implementations for std
710
#[cfg(feature = "std")]
711
extern "C" fn default_thread_destructor_fn(thread: *mut ThreadInner) {
712
    let thread = unsafe { &mut *thread };
713

            
714
    if let Some(thread_handle) = thread.thread_handle.take() {
715
        let _ = thread.sender.send(ThreadSendMsg::TerminateThread);
716
        let _ = thread_handle.join(); // ignore the result, don't panic
717
    }
718
}
719

            
720
#[cfg(not(feature = "std"))]
721
extern "C" fn default_thread_destructor_fn(_thread: *mut ThreadInner) {}
722

            
723
#[cfg(feature = "std")]
724
extern "C" fn library_send_thread_msg_fn(
725
    sender: *const core::ffi::c_void,
726
    msg: ThreadSendMsg,
727
) -> bool {
728
    unsafe { &*(sender as *const Sender<ThreadSendMsg>) }
729
        .send(msg)
730
        .is_ok()
731
}
732

            
733
#[cfg(not(feature = "std"))]
734
extern "C" fn library_send_thread_msg_fn(
735
    _sender: *const core::ffi::c_void,
736
    _msg: ThreadSendMsg,
737
) -> bool {
738
    false
739
}
740

            
741
#[cfg(feature = "std")]
742
extern "C" fn library_receive_thread_msg_fn(
743
    receiver: *const core::ffi::c_void,
744
) -> OptionThreadReceiveMsg {
745
    unsafe { &*(receiver as *const Receiver<ThreadReceiveMsg>) }
746
        .try_recv()
747
        .ok()
748
        .into()
749
}
750

            
751
#[cfg(not(feature = "std"))]
752
extern "C" fn library_receive_thread_msg_fn(
753
    _receiver: *const core::ffi::c_void,
754
) -> OptionThreadReceiveMsg {
755
    None.into()
756
}
757

            
758
#[cfg(feature = "std")]
759
extern "C" fn default_send_thread_msg_fn(
760
    sender: *const core::ffi::c_void,
761
    msg: ThreadReceiveMsg,
762
) -> bool {
763
    unsafe { &*(sender as *const Sender<ThreadReceiveMsg>) }
764
        .send(msg)
765
        .is_ok()
766
}
767

            
768
#[cfg(not(feature = "std"))]
769
extern "C" fn default_send_thread_msg_fn(
770
    _sender: *const core::ffi::c_void,
771
    _msg: ThreadReceiveMsg,
772
) -> bool {
773
    false
774
}
775

            
776
#[cfg(feature = "std")]
777
extern "C" fn default_receive_thread_msg_fn(
778
    receiver: *const core::ffi::c_void,
779
) -> OptionThreadSendMsg {
780
    unsafe { &*(receiver as *const Receiver<ThreadSendMsg>) }
781
        .try_recv()
782
        .ok()
783
        .into()
784
}
785

            
786
#[cfg(not(feature = "std"))]
787
extern "C" fn default_receive_thread_msg_fn(
788
    _receiver: *const core::ffi::c_void,
789
) -> OptionThreadSendMsg {
790
    None.into()
791
}
792

            
793
#[cfg(feature = "std")]
794
extern "C" fn default_check_thread_finished(dropcheck: *const core::ffi::c_void) -> bool {
795
    let weak = unsafe { &*(dropcheck as *const alloc::sync::Weak<()>) };
796
    weak.upgrade().is_none()
797
}
798

            
799
#[cfg(not(feature = "std"))]
800
extern "C" fn default_check_thread_finished(_dropcheck: *const core::ffi::c_void) -> bool {
801
    true
802
}
803

            
804
#[cfg(feature = "std")]
805
extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
806

            
807
#[cfg(not(feature = "std"))]
808
extern "C" fn thread_sender_drop(_: *mut ThreadSenderInner) {}
809

            
810
#[cfg(feature = "std")]
811
extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
812

            
813
#[cfg(not(feature = "std"))]
814
extern "C" fn thread_receiver_drop(_: *mut ThreadReceiverInner) {}
815

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

            
819
#[repr(C)]
820
pub struct CreateThreadCallback {
821
    pub cb: CreateThreadCallbackType,
822
}
823

            
824
impl core::fmt::Debug for CreateThreadCallback {
825
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
826
        write!(
827
            f,
828
            "CreateThreadCallback {{ cb: {:p} }}",
829
            self.cb as *const ()
830
        )
831
    }
832
}
833

            
834
impl Clone for CreateThreadCallback {
835
    fn clone(&self) -> Self {
836
        Self { cb: self.cb }
837
    }
838
}
839

            
840
impl Copy for CreateThreadCallback {}
841

            
842
impl PartialEq for CreateThreadCallback {
843
    fn eq(&self, other: &Self) -> bool {
844
        self.cb as usize == other.cb as usize
845
    }
846
}
847

            
848
impl Eq for CreateThreadCallback {}
849

            
850
impl PartialOrd for CreateThreadCallback {
851
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
852
        (self.cb as usize).partial_cmp(&(other.cb as usize))
853
    }
854
}
855

            
856
impl Ord for CreateThreadCallback {
857
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
858
        (self.cb as usize).cmp(&(other.cb as usize))
859
    }
860
}
861

            
862
impl core::hash::Hash for CreateThreadCallback {
863
    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
864
        (self.cb as usize).hash(state);
865
    }
866
}
867

            
868
/// Create a new thread using the standard library
869
#[cfg(feature = "std")]
870
pub extern "C" fn create_thread_libstd(
871
    thread_initialize_data: RefAny,
872
    writeback_data: RefAny,
873
    callback: ThreadCallback,
874
) -> Thread {
875
    let (sender_receiver, receiver_receiver) = channel::<ThreadReceiveMsg>();
876
    let mut sender_receiver = ThreadSender::new(ThreadSenderInner {
877
        ptr: alloc::boxed::Box::new(sender_receiver),
878
        send_fn: ThreadSendCallback {
879
            cb: default_send_thread_msg_fn,
880
        },
881
        destructor: ThreadSenderDestructorCallback {
882
            cb: thread_sender_drop,
883
        },
884
    });
885
    // Set the ctx from the callback for FFI
886
    sender_receiver.ctx = callback.ctx.clone();
887

            
888
    let (sender_sender, receiver_sender) = channel::<ThreadSendMsg>();
889
    let mut receiver_sender = ThreadReceiver::new(ThreadReceiverInner {
890
        ptr: alloc::boxed::Box::new(receiver_sender),
891
        recv_fn: ThreadRecvCallback {
892
            cb: default_receive_thread_msg_fn,
893
        },
894
        destructor: ThreadReceiverDestructorCallback {
895
            cb: thread_receiver_drop,
896
        },
897
    });
898
    // Set the ctx from the callback for FFI
899
    receiver_sender.ctx = callback.ctx.clone();
900

            
901
    let thread_check = Arc::new(());
902
    let dropcheck = Arc::downgrade(&thread_check);
903

            
904
    let thread_handle = Some(thread::spawn(move || {
905
        // Keep thread_check alive for the entire duration of the thread
906
        // by binding it to a named variable (not `_` which drops immediately)
907
        let _thread_check_guard = thread_check;
908
        (callback.cb)(thread_initialize_data, sender_receiver, receiver_sender);
909
        // _thread_check_guard gets dropped here, signals that the thread has finished
910
    }));
911

            
912
    let thread_handle: alloc::boxed::Box<Option<JoinHandle<()>>> =
913
        alloc::boxed::Box::new(thread_handle);
914
    let sender: alloc::boxed::Box<Sender<ThreadSendMsg>> = alloc::boxed::Box::new(sender_sender);
915
    let receiver: alloc::boxed::Box<Receiver<ThreadReceiveMsg>> =
916
        alloc::boxed::Box::new(receiver_receiver);
917
    let dropcheck: alloc::boxed::Box<alloc::sync::Weak<()>> = alloc::boxed::Box::new(dropcheck);
918

            
919
    Thread::new(ThreadInner {
920
        thread_handle,
921
        sender,
922
        receiver,
923
        writeback_data,
924
        dropcheck,
925
        thread_destructor_fn: ThreadDestructorCallback {
926
            cb: default_thread_destructor_fn,
927
        },
928
        check_thread_finished_fn: CheckThreadFinishedCallback {
929
            cb: default_check_thread_finished,
930
        },
931
        send_thread_msg_fn: LibrarySendThreadMsgCallback {
932
            cb: library_send_thread_msg_fn,
933
        },
934
        receive_thread_msg_fn: LibraryReceiveThreadMsgCallback {
935
            cb: library_receive_thread_msg_fn,
936
        },
937
    })
938
}
939

            
940
#[cfg(not(feature = "std"))]
941
pub extern "C" fn create_thread_libstd(
942
    _thread_initialize_data: RefAny,
943
    _writeback_data: RefAny,
944
    _callback: ThreadCallback,
945
) -> Thread {
946
    Thread {
947
        ptr: core::ptr::null(),
948
        run_destructor: false,
949
    }
950
}
951

            
952
#[cfg(test)]
953
mod tests {
954
    use super::*;
955

            
956
    extern "C" fn test_writeback_callback(
957
        _thread_data: RefAny,
958
        _writeback_data: RefAny,
959
        _callback_info: CallbackInfo,
960
    ) -> Update {
961
        Update::DoNothing
962
    }
963

            
964
    #[test]
965
1
    fn test_writeback_callback_creation() {
966
1
        let callback = WriteBackCallback::new(test_writeback_callback);
967
1
        assert_eq!(callback.cb as usize, test_writeback_callback as usize);
968
1
    }
969

            
970
    #[test]
971
1
    fn test_writeback_callback_clone() {
972
1
        let callback = WriteBackCallback::new(test_writeback_callback);
973
1
        let cloned = callback.clone();
974
1
        assert_eq!(callback, cloned);
975
1
    }
976
}
977

            
978
/// Optional Thread type for API compatibility
979
#[derive(Debug, Clone)]
980
#[repr(C, u8)]
981
pub enum OptionThread {
982
    None,
983
    Some(Thread),
984
}
985

            
986
impl From<Option<Thread>> for OptionThread {
987
    fn from(o: Option<Thread>) -> Self {
988
        match o {
989
            None => OptionThread::None,
990
            Some(t) => OptionThread::Some(t),
991
        }
992
    }
993
}
994

            
995
impl OptionThread {
996
    pub fn into_option(self) -> Option<Thread> {
997
        match self {
998
            OptionThread::None => None,
999
            OptionThread::Some(t) => Some(t),
        }
    }
}
// ============================================================================
// Sleep utilities
// ============================================================================
/// Sleeps the current thread for the specified number of milliseconds.
///
/// This is a cross-platform utility that can be called from C/C++/Python.
///
/// # Arguments
/// * `milliseconds` - Number of milliseconds to sleep
#[cfg(feature = "std")]
pub fn thread_sleep_ms(milliseconds: u64) -> azul_css::corety::EmptyStruct {
    std::thread::sleep(std::time::Duration::from_millis(milliseconds));
    azul_css::corety::EmptyStruct::new()
}
/// Sleeps the current thread for the specified number of milliseconds (no-op on no_std).
#[cfg(not(feature = "std"))]
pub fn thread_sleep_ms(_milliseconds: 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 microseconds.
///
/// # 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()
}