1use std::mem;
4
5use crate::ffi::{self, gboolean, gpointer};
6
7use crate::{MainContext, Source, SourceId, source::Priority, translate::*};
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 {
109 match self.acquire() {
110 Ok(_acquire) => {
111 func();
112 }
113 _ => {
114 panic!("Must be called from a thread that owns the main context");
115 }
116 }
117 }
118 }
119
120 unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
121 where
122 F: FnOnce() + 'static,
123 {
124 unsafe {
125 unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
126 unsafe {
127 let func: &mut Option<F> = &mut *(func as *mut Option<F>);
128 let func = func
129 .take()
130 .expect("MainContext::invoke() closure called multiple times");
131 func();
132 ffi::G_SOURCE_REMOVE
133 }
134 }
135 unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
136 unsafe {
137 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
138 }
139 }
140 let func = Box::into_raw(Box::new(Some(func)));
141 ffi::g_main_context_invoke_full(
142 self.to_glib_none().0,
143 priority.into_glib(),
144 Some(trampoline::<F>),
145 func as gpointer,
146 Some(destroy_closure::<F>),
147 )
148 }
149 }
150
151 #[doc(alias = "g_main_context_push_thread_default")]
159 pub fn with_thread_default<R, F: FnOnce() -> R + Sized>(
160 &self,
161 func: F,
162 ) -> Result<R, crate::BoolError> {
163 let _acquire = self.acquire()?;
164 let _thread_default = ThreadDefaultContext::new(self);
165 Ok(func())
166 }
167
168 #[doc(alias = "g_main_context_acquire")]
175 pub fn acquire(&self) -> Result<MainContextAcquireGuard<'_>, crate::BoolError> {
176 unsafe {
177 let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
178 if ret {
179 Ok(MainContextAcquireGuard(self))
180 } else {
181 Err(bool_error!(
182 "Failed to acquire ownership of main context, already acquired by another thread"
183 ))
184 }
185 }
186 }
187}
188
189#[must_use = "if unused the main context will be released immediately"]
190pub struct MainContextAcquireGuard<'a>(&'a MainContext);
191
192impl Drop for MainContextAcquireGuard<'_> {
193 #[doc(alias = "g_main_context_release")]
194 #[inline]
195 fn drop(&mut self) {
196 unsafe {
197 ffi::g_main_context_release(self.0.to_glib_none().0);
198 }
199 }
200}
201
202struct ThreadDefaultContext<'a>(&'a MainContext);
203
204impl ThreadDefaultContext<'_> {
205 fn new(ctx: &MainContext) -> ThreadDefaultContext<'_> {
206 unsafe {
207 ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
208 }
209 ThreadDefaultContext(ctx)
210 }
211}
212
213impl Drop for ThreadDefaultContext<'_> {
214 #[inline]
215 fn drop(&mut self) {
216 unsafe {
217 ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use std::{panic, ptr, thread};
225
226 use super::*;
227
228 #[test]
229 fn test_invoke() {
230 let c = MainContext::new();
231 let l = crate::MainLoop::new(Some(&c), false);
232
233 let l_clone = l.clone();
234 let join_handle = thread::spawn(move || {
235 c.invoke(move || l_clone.quit());
236 });
237
238 l.run();
239
240 join_handle.join().unwrap();
241 }
242
243 fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
244 ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
245 }
246
247 #[test]
248 fn test_with_thread_default() {
249 let a = MainContext::new();
250 let b = MainContext::new();
251
252 assert!(!is_same_context(&a, &b));
253
254 a.with_thread_default(|| {
255 let t = MainContext::thread_default().unwrap();
256 assert!(is_same_context(&a, &t));
257
258 b.with_thread_default(|| {
259 let t = MainContext::thread_default().unwrap();
260 assert!(is_same_context(&b, &t));
261 })
262 .unwrap();
263
264 let t = MainContext::thread_default().unwrap();
265 assert!(is_same_context(&a, &t));
266 })
267 .unwrap();
268 }
269
270 #[test]
271 fn test_with_thread_default_is_panic_safe() {
272 let a = MainContext::new();
273 let b = MainContext::new();
274
275 assert!(!is_same_context(&a, &b));
276
277 a.with_thread_default(|| {
278 let t = MainContext::thread_default().unwrap();
279 assert!(is_same_context(&a, &t));
280
281 let result = panic::catch_unwind(|| {
282 b.with_thread_default(|| {
283 panic!();
284 })
285 .unwrap();
286 });
287 assert!(result.is_err());
288
289 let t = MainContext::thread_default().unwrap();
290 assert!(is_same_context(&a, &t));
291 })
292 .unwrap();
293 }
294}