1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::fmt::Debug;
use crate::{
get_system_thread_id, main_thread::MainThreadFacilitator, platform::PlatformRunLoopSender,
util::BlockingVariable, RunLoop, SystemThreadId,
};
// Can be used to send callbacks from other threads to be executed on run loop thread
#[derive(Clone)]
pub struct RunLoopSender {
inner: RunLoopSenderInner,
}
#[derive(Clone)]
enum RunLoopSenderInner {
PlatformSender {
thread_id: SystemThreadId,
platform_sender: PlatformRunLoopSender,
},
MainThreadSender,
}
impl Debug for RunLoopSender {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.inner {
RunLoopSenderInner::PlatformSender {
thread_id,
platform_sender: _,
} => f
.debug_struct("RunLoopSender")
.field("thread_id", &thread_id)
.finish(),
RunLoopSenderInner::MainThreadSender => f
.debug_struct("RunLoopSender")
.field("thread_id", &"main")
.finish(),
}
}
}
impl RunLoopSender {
pub(crate) fn new(platform_sender: PlatformRunLoopSender) -> Self {
Self {
inner: RunLoopSenderInner::PlatformSender {
thread_id: get_system_thread_id(),
platform_sender,
},
}
}
/// Creates sender for main thread. This should only be called from
/// background threads. On main thread the RunLoop should create regular
/// sender from current run loop.
///
/// The reason is that the main thread sender, when invoking on main thread,
/// may execute the callback synchronously instead of scheduling it (linux),
/// which is not how regular run loop sender works.
#[allow(unused)] // not used in tests
pub(crate) fn new_for_main_thread() -> Self {
debug_assert!(!RunLoop::is_main_thread().unwrap_or(true));
Self {
inner: RunLoopSenderInner::MainThreadSender,
}
}
/// Returns true if sender would send the callback to current thread.
pub fn is_same_thread(&self) -> bool {
match self.inner {
RunLoopSenderInner::PlatformSender {
thread_id,
platform_sender: _,
} => get_system_thread_id() == thread_id,
// This should never panic as we check for whether engine context plugin is loaded
// before creating the sender.
RunLoopSenderInner::MainThreadSender => RunLoop::is_main_thread().unwrap(),
}
}
/// Schedules the callback to be executed on run loop and returns immediately.
pub fn send<F>(&self, callback: F)
where
F: FnOnce() + 'static + Send,
{
match &self.inner {
RunLoopSenderInner::PlatformSender {
thread_id: _,
platform_sender,
} => {
platform_sender.send(callback);
}
RunLoopSenderInner::MainThreadSender => {
// This should never panic as we check for whether engine context plugin is loaded
// before creating the sender.
MainThreadFacilitator::get()
.perform_on_main_thread(callback)
.unwrap();
}
}
}
/// Schedules the callback on run loop and blocks until it is invoked.
/// If current thread is run loop thread the callback will be invoked immediately
/// (otherwise it would deadlock).
pub fn send_and_wait<F, R>(&self, callback: F) -> R
where
F: FnOnce() -> R + 'static + Send,
R: Send + 'static,
{
if self.is_same_thread() {
callback()
} else {
let var = BlockingVariable::<R>::new();
let var_clone = var.clone();
self.send(move || {
var_clone.set(callback());
});
var.get_blocking()
}
}
}