1use std::mem;
4
5use crate::ffi::{self, gboolean, gpointer};
6
7use crate::{source::Priority, translate::*, MainContext, Source, SourceId};
8
9impl MainContext {
10 #[doc(alias = "g_main_context_prepare")]
11 pub fn prepare(&self) -> (bool, i32) {
12 unsafe {
13 let mut priority = mem::MaybeUninit::uninit();
14
15 let res = from_glib(ffi::g_main_context_prepare(
16 self.to_glib_none().0,
17 priority.as_mut_ptr(),
18 ));
19 let priority = priority.assume_init();
20 (res, priority)
21 }
22 }
23
24 #[doc(alias = "g_main_context_find_source_by_id")]
25 pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
26 unsafe {
27 from_glib_none(ffi::g_main_context_find_source_by_id(
28 self.to_glib_none().0,
29 source_id.as_raw(),
30 ))
31 }
32 }
33
34 #[doc(alias = "g_main_context_invoke")]
42 pub fn invoke<F>(&self, func: F)
43 where
44 F: FnOnce() + Send + 'static,
45 {
46 self.invoke_with_priority(crate::Priority::DEFAULT_IDLE, func);
47 }
48
49 #[doc(alias = "g_main_context_invoke_full")]
57 pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
58 where
59 F: FnOnce() + Send + 'static,
60 {
61 unsafe {
62 self.invoke_unsafe(priority, func);
63 }
64 }
65
66 pub fn invoke_local<F>(&self, func: F)
80 where
81 F: FnOnce() + 'static,
82 {
83 self.invoke_local_with_priority(crate::Priority::DEFAULT_IDLE, func);
84 }
85
86 #[allow(clippy::if_same_then_else)]
100 pub fn invoke_local_with_priority<F>(&self, _priority: Priority, func: F)
101 where
102 F: FnOnce() + 'static,
103 {
104 if self.is_owner() {
107 func();
108 } else if let Ok(_acquire) = self.acquire() {
109 func();
110 } else {
111 panic!("Must be called from a thread that owns the main context");
112 }
113 }
114
115 unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
116 where
117 F: FnOnce() + 'static,
118 {
119 unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
120 let func: &mut Option<F> = &mut *(func as *mut Option<F>);
121 let func = func
122 .take()
123 .expect("MainContext::invoke() closure called multiple times");
124 func();
125 ffi::G_SOURCE_REMOVE
126 }
127 unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
128 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
129 }
130 let func = Box::into_raw(Box::new(Some(func)));
131 ffi::g_main_context_invoke_full(
132 self.to_glib_none().0,
133 priority.into_glib(),
134 Some(trampoline::<F>),
135 func as gpointer,
136 Some(destroy_closure::<F>),
137 )
138 }
139
140 #[doc(alias = "g_main_context_push_thread_default")]
148 pub fn with_thread_default<R, F: FnOnce() -> R + Sized>(
149 &self,
150 func: F,
151 ) -> Result<R, crate::BoolError> {
152 let _acquire = self.acquire()?;
153 let _thread_default = ThreadDefaultContext::new(self);
154 Ok(func())
155 }
156
157 #[doc(alias = "g_main_context_acquire")]
164 pub fn acquire(&self) -> Result<MainContextAcquireGuard<'_>, crate::BoolError> {
165 unsafe {
166 let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
167 if ret {
168 Ok(MainContextAcquireGuard(self))
169 } else {
170 Err(bool_error!("Failed to acquire ownership of main context, already acquired by another thread"))
171 }
172 }
173 }
174}
175
176#[must_use = "if unused the main context will be released immediately"]
177pub struct MainContextAcquireGuard<'a>(&'a MainContext);
178
179impl Drop for MainContextAcquireGuard<'_> {
180 #[doc(alias = "g_main_context_release")]
181 #[inline]
182 fn drop(&mut self) {
183 unsafe {
184 ffi::g_main_context_release(self.0.to_glib_none().0);
185 }
186 }
187}
188
189struct ThreadDefaultContext<'a>(&'a MainContext);
190
191impl ThreadDefaultContext<'_> {
192 fn new(ctx: &MainContext) -> ThreadDefaultContext<'_> {
193 unsafe {
194 ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
195 }
196 ThreadDefaultContext(ctx)
197 }
198}
199
200impl Drop for ThreadDefaultContext<'_> {
201 #[inline]
202 fn drop(&mut self) {
203 unsafe {
204 ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
205 }
206 }
207}
208
209#[cfg(test)]
210mod tests {
211 use std::{panic, ptr, thread};
212
213 use super::*;
214
215 #[test]
216 fn test_invoke() {
217 let c = MainContext::new();
218 let l = crate::MainLoop::new(Some(&c), false);
219
220 let l_clone = l.clone();
221 let join_handle = thread::spawn(move || {
222 c.invoke(move || l_clone.quit());
223 });
224
225 l.run();
226
227 join_handle.join().unwrap();
228 }
229
230 fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
231 ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
232 }
233
234 #[test]
235 fn test_with_thread_default() {
236 let a = MainContext::new();
237 let b = MainContext::new();
238
239 assert!(!is_same_context(&a, &b));
240
241 a.with_thread_default(|| {
242 let t = MainContext::thread_default().unwrap();
243 assert!(is_same_context(&a, &t));
244
245 b.with_thread_default(|| {
246 let t = MainContext::thread_default().unwrap();
247 assert!(is_same_context(&b, &t));
248 })
249 .unwrap();
250
251 let t = MainContext::thread_default().unwrap();
252 assert!(is_same_context(&a, &t));
253 })
254 .unwrap();
255 }
256
257 #[test]
258 fn test_with_thread_default_is_panic_safe() {
259 let a = MainContext::new();
260 let b = MainContext::new();
261
262 assert!(!is_same_context(&a, &b));
263
264 a.with_thread_default(|| {
265 let t = MainContext::thread_default().unwrap();
266 assert!(is_same_context(&a, &t));
267
268 let result = panic::catch_unwind(|| {
269 b.with_thread_default(|| {
270 panic!();
271 })
272 .unwrap();
273 });
274 assert!(result.is_err());
275
276 let t = MainContext::thread_default().unwrap();
277 assert!(is_same_context(&a, &t));
278 })
279 .unwrap();
280 }
281}