glib/main_context.rs
1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::mem;
4
5use crate::ffi::{self, gboolean, gpointer};
6
7use crate::{MainContext, Source, SourceId, source::Priority, translate::*};
8
9impl MainContext {
10 /// Prepares to poll sources within a main loop.
11 ///
12 /// The resulting information
13 /// for polling is determined by calling `GLib::MainContext::query()`.
14 ///
15 /// You must have successfully acquired the context with
16 /// [`acquire()`][Self::acquire()] before you may call this function.
17 ///
18 /// # Returns
19 ///
20 /// true if some source is ready to be dispatched prior to polling,
21 /// false otherwise
22 ///
23 /// ## `priority`
24 /// location to store priority of highest priority
25 /// source already ready
26 // rustdoc-stripper-ignore-next-stop
27 /// Prepares to poll sources within a main loop.
28 ///
29 /// The resulting information
30 /// for polling is determined by calling `GLib::MainContext::query()`.
31 ///
32 /// You must have successfully acquired the context with
33 /// [`acquire()`][Self::acquire()] before you may call this function.
34 ///
35 /// # Returns
36 ///
37 /// true if some source is ready to be dispatched prior to polling,
38 /// false otherwise
39 ///
40 /// ## `priority`
41 /// location to store priority of highest priority
42 /// source already ready
43 #[doc(alias = "g_main_context_prepare")]
44 pub fn prepare(&self) -> (bool, i32) {
45 unsafe {
46 let mut priority = mem::MaybeUninit::uninit();
47
48 let res = from_glib(ffi::g_main_context_prepare(
49 self.to_glib_none().0,
50 priority.as_mut_ptr(),
51 ));
52 let priority = priority.assume_init();
53 (res, priority)
54 }
55 }
56
57 /// Finds a [`Source`][crate::Source] given a pair of context and ID.
58 ///
59 /// It is a programmer error to attempt to look up a non-existent source.
60 ///
61 /// More specifically: source IDs can be reissued after a source has been
62 /// destroyed and therefore it is never valid to use this function with a
63 /// source ID which may have already been removed. An example is when
64 /// scheduling an idle to run in another thread with `idle_add()`: the
65 /// idle may already have run and been removed by the time this function
66 /// is called on its (now invalid) source ID. This source ID may have
67 /// been reissued, leading to the operation being performed against the
68 /// wrong source.
69 /// ## `source_id`
70 /// the source ID, as returned by `GLib::Source::get_id()`
71 ///
72 /// # Returns
73 ///
74 /// the source
75 // rustdoc-stripper-ignore-next-stop
76 /// Finds a [`Source`][crate::Source] given a pair of context and ID.
77 ///
78 /// It is a programmer error to attempt to look up a non-existent source.
79 ///
80 /// More specifically: source IDs can be reissued after a source has been
81 /// destroyed and therefore it is never valid to use this function with a
82 /// source ID which may have already been removed. An example is when
83 /// scheduling an idle to run in another thread with `idle_add()`: the
84 /// idle may already have run and been removed by the time this function
85 /// is called on its (now invalid) source ID. This source ID may have
86 /// been reissued, leading to the operation being performed against the
87 /// wrong source.
88 /// ## `source_id`
89 /// the source ID, as returned by `GLib::Source::get_id()`
90 ///
91 /// # Returns
92 ///
93 /// the source
94 #[doc(alias = "g_main_context_find_source_by_id")]
95 pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
96 unsafe {
97 from_glib_none(ffi::g_main_context_find_source_by_id(
98 self.to_glib_none().0,
99 source_id.as_raw(),
100 ))
101 }
102 }
103
104 // rustdoc-stripper-ignore-next
105 /// Invokes `func` on the main context.
106 ///
107 /// If the current thread is the owner of the main context or the main context currently has no
108 /// owner then `func` will be called directly from inside this function. If this behaviour is
109 /// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
110 /// [`glib::idle_add`](crate::idle_add) instead.
111 // rustdoc-stripper-ignore-next-stop
112 /// Invokes a function in such a way that @self is owned during the
113 /// invocation of @function.
114 ///
115 /// If @self is `NULL` then the global-default main context — as
116 /// returned by [`default()`][Self::default()] — is used.
117 ///
118 /// If @self is owned by the current thread, @function is called
119 /// directly. Otherwise, if @self is the thread-default main context
120 /// of the current thread and [`acquire()`][Self::acquire()] succeeds,
121 /// then @function is called and [`release()`][Self::release()] is called
122 /// afterwards.
123 ///
124 /// In any other case, an idle source is created to call @function and
125 /// that source is attached to @self (presumably to be run in another
126 /// thread). The idle source is attached with `GLib::PRIORITY_DEFAULT`
127 /// priority. If you want a different priority, use
128 /// [`invoke_full()`][Self::invoke_full()].
129 ///
130 /// Note that, as with normal idle functions, @function should probably return
131 /// `GLib::SOURCE_REMOVE`. If it returns `GLib::SOURCE_CONTINUE`, it
132 /// will be continuously run in a loop (and may prevent this call from returning).
133 /// ## `function`
134 /// function to call
135 // rustdoc-stripper-ignore-next-stop
136 /// Invokes a function in such a way that @self is owned during the
137 /// invocation of @function.
138 ///
139 /// If @self is `NULL` then the global-default main context — as
140 /// returned by [`default()`][Self::default()] — is used.
141 ///
142 /// If @self is owned by the current thread, @function is called
143 /// directly. Otherwise, if @self is the thread-default main context
144 /// of the current thread and [`acquire()`][Self::acquire()] succeeds,
145 /// then @function is called and [`release()`][Self::release()] is called
146 /// afterwards.
147 ///
148 /// In any other case, an idle source is created to call @function and
149 /// that source is attached to @self (presumably to be run in another
150 /// thread). The idle source is attached with `GLib::PRIORITY_DEFAULT`
151 /// priority. If you want a different priority, use
152 /// [`invoke_full()`][Self::invoke_full()].
153 ///
154 /// Note that, as with normal idle functions, @function should probably return
155 /// `GLib::SOURCE_REMOVE`. If it returns `GLib::SOURCE_CONTINUE`, it
156 /// will be continuously run in a loop (and may prevent this call from returning).
157 /// ## `function`
158 /// function to call
159 #[doc(alias = "g_main_context_invoke")]
160 pub fn invoke<F>(&self, func: F)
161 where
162 F: FnOnce() + Send + 'static,
163 {
164 self.invoke_with_priority(crate::Priority::DEFAULT_IDLE, func);
165 }
166
167 // rustdoc-stripper-ignore-next
168 /// Invokes `func` on the main context with the given priority.
169 ///
170 /// If the current thread is the owner of the main context or the main context currently has no
171 /// owner then `func` will be called directly from inside this function. If this behaviour is
172 /// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
173 /// [`glib::idle_add`](crate::idle_add) instead.
174 #[doc(alias = "g_main_context_invoke_full")]
175 pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
176 where
177 F: FnOnce() + Send + 'static,
178 {
179 unsafe {
180 self.invoke_unsafe(priority, func);
181 }
182 }
183
184 // rustdoc-stripper-ignore-next
185 /// Invokes `func` on the main context.
186 ///
187 /// Different to `invoke()`, this does not require `func` to be
188 /// `Send` but can only be called from the thread that owns the main context.
189 ///
190 /// This function panics if called from a different thread than the one that
191 /// owns the main context.
192 ///
193 /// Note that this effectively means that `func` is called directly from inside this function
194 /// or otherwise panics immediately. If this behaviour is not desired and `func` should always
195 /// be called asynchronously then use [`MainContext::spawn_local`]
196 /// [`glib::idle_add_local`](crate::idle_add_local) instead.
197 pub fn invoke_local<F>(&self, func: F)
198 where
199 F: FnOnce() + 'static,
200 {
201 self.invoke_local_with_priority(crate::Priority::DEFAULT_IDLE, func);
202 }
203
204 // rustdoc-stripper-ignore-next
205 /// Invokes `func` on the main context with the given priority.
206 ///
207 /// Different to `invoke_with_priority()`, this does not require `func` to be
208 /// `Send` but can only be called from the thread that owns the main context.
209 ///
210 /// This function panics if called from a different thread than the one that
211 /// owns the main context.
212 ///
213 /// Note that this effectively means that `func` is called directly from inside this function
214 /// or otherwise panics immediately. If this behaviour is not desired and `func` should always
215 /// be called asynchronously then use [`MainContext::spawn_local`]
216 /// [`glib::idle_add_local`](crate::idle_add_local) instead.
217 #[allow(clippy::if_same_then_else)]
218 pub fn invoke_local_with_priority<F>(&self, _priority: Priority, func: F)
219 where
220 F: FnOnce() + 'static,
221 {
222 // Checks from `g_main_context_invoke_full()`
223 // FIXME: Combine the first two cases somehow
224 if self.is_owner() {
225 func();
226 } else {
227 match self.acquire() {
228 Ok(_acquire) => {
229 func();
230 }
231 _ => {
232 panic!("Must be called from a thread that owns the main context");
233 }
234 }
235 }
236 }
237
238 unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
239 where
240 F: FnOnce() + 'static,
241 {
242 unsafe {
243 unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
244 unsafe {
245 let func: &mut Option<F> = &mut *(func as *mut Option<F>);
246 let func = func
247 .take()
248 .expect("MainContext::invoke() closure called multiple times");
249 func();
250 ffi::G_SOURCE_REMOVE
251 }
252 }
253 unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
254 unsafe {
255 let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
256 }
257 }
258 let func = Box::into_raw(Box::new(Some(func)));
259 ffi::g_main_context_invoke_full(
260 self.to_glib_none().0,
261 priority.into_glib(),
262 Some(trampoline::<F>),
263 func as gpointer,
264 Some(destroy_closure::<F>),
265 )
266 }
267 }
268
269 // rustdoc-stripper-ignore-next
270 /// Call closure with the main context configured as the thread default one.
271 ///
272 /// The thread default main context is changed in a panic-safe manner before calling `func` and
273 /// released again afterwards regardless of whether closure panicked or not.
274 ///
275 /// This will fail if the main context is owned already by another thread.
276 // rustdoc-stripper-ignore-next-stop
277 /// Acquires @self and sets it as the thread-default context for the
278 /// current thread. This will cause certain asynchronous operations
279 /// (such as most [Gio](../gio/index.html)-based I/O) which are
280 /// started in this thread to run under @self and deliver their
281 /// results to its main loop, rather than running under the global
282 /// default main context in the main thread. Note that calling this function
283 /// changes the context returned by [`thread_default()`][Self::thread_default()],
284 /// not the one returned by [`default()`][Self::default()], so it does not
285 /// affect the context used by functions like `idle_add()`.
286 ///
287 /// Normally you would call this function shortly after creating a new
288 /// thread, passing it a [`MainContext`][crate::MainContext] which will be run by a
289 /// [`MainLoop`][crate::MainLoop] in that thread, to set a new default context for all
290 /// async operations in that thread. In this case you may not need to
291 /// ever call [`pop_thread_default()`][Self::pop_thread_default()], assuming you want
292 /// the new [`MainContext`][crate::MainContext] to be the default for the whole lifecycle
293 /// of the thread.
294 ///
295 /// If you don’t have control over how the new thread was created (e.g.
296 /// in the new thread isn’t newly created, or if the thread life
297 /// cycle is managed by a #GThreadPool), it is always suggested to wrap
298 /// the logic that needs to use the new [`MainContext`][crate::MainContext] inside a
299 /// [`with_thread_default()`][Self::with_thread_default()] /
300 /// [`pop_thread_default()`][Self::pop_thread_default()] pair, otherwise threads that
301 /// are re-used will end up never explicitly releasing the
302 /// [`MainContext`][crate::MainContext] reference they hold.
303 ///
304 /// In some cases you may want to schedule a single operation in a
305 /// non-default context, or temporarily use a non-default context in
306 /// the main thread. In that case, you can wrap the call to the
307 /// asynchronous operation inside a
308 /// [`with_thread_default()`][Self::with_thread_default()] /
309 /// [`pop_thread_default()`][Self::pop_thread_default()] pair, but it is up to you to
310 /// ensure that no other asynchronous operations accidentally get
311 /// started while the non-default context is active.
312 ///
313 /// Beware that libraries that predate this function may not correctly
314 /// handle being used from a thread with a thread-default context. For example,
315 /// see `g_file_supports_thread_contexts()`.
316 // rustdoc-stripper-ignore-next-stop
317 /// Acquires @self and sets it as the thread-default context for the
318 /// current thread. This will cause certain asynchronous operations
319 /// (such as most [Gio](../gio/index.html)-based I/O) which are
320 /// started in this thread to run under @self and deliver their
321 /// results to its main loop, rather than running under the global
322 /// default main context in the main thread. Note that calling this function
323 /// changes the context returned by [`thread_default()`][Self::thread_default()],
324 /// not the one returned by [`default()`][Self::default()], so it does not
325 /// affect the context used by functions like `idle_add()`.
326 ///
327 /// Normally you would call this function shortly after creating a new
328 /// thread, passing it a [`MainContext`][crate::MainContext] which will be run by a
329 /// [`MainLoop`][crate::MainLoop] in that thread, to set a new default context for all
330 /// async operations in that thread. In this case you may not need to
331 /// ever call [`pop_thread_default()`][Self::pop_thread_default()], assuming you want
332 /// the new [`MainContext`][crate::MainContext] to be the default for the whole lifecycle
333 /// of the thread.
334 ///
335 /// If you don’t have control over how the new thread was created (e.g.
336 /// in the new thread isn’t newly created, or if the thread life
337 /// cycle is managed by a #GThreadPool), it is always suggested to wrap
338 /// the logic that needs to use the new [`MainContext`][crate::MainContext] inside a
339 /// [`with_thread_default()`][Self::with_thread_default()] /
340 /// [`pop_thread_default()`][Self::pop_thread_default()] pair, otherwise threads that
341 /// are re-used will end up never explicitly releasing the
342 /// [`MainContext`][crate::MainContext] reference they hold.
343 ///
344 /// In some cases you may want to schedule a single operation in a
345 /// non-default context, or temporarily use a non-default context in
346 /// the main thread. In that case, you can wrap the call to the
347 /// asynchronous operation inside a
348 /// [`with_thread_default()`][Self::with_thread_default()] /
349 /// [`pop_thread_default()`][Self::pop_thread_default()] pair, but it is up to you to
350 /// ensure that no other asynchronous operations accidentally get
351 /// started while the non-default context is active.
352 ///
353 /// Beware that libraries that predate this function may not correctly
354 /// handle being used from a thread with a thread-default context. For example,
355 /// see `g_file_supports_thread_contexts()`.
356 #[doc(alias = "g_main_context_push_thread_default")]
357 pub fn with_thread_default<R, F: FnOnce() -> R + Sized>(
358 &self,
359 func: F,
360 ) -> Result<R, crate::BoolError> {
361 let _acquire = self.acquire()?;
362 let _thread_default = ThreadDefaultContext::new(self);
363 Ok(func())
364 }
365
366 // rustdoc-stripper-ignore-next
367 /// Acquire ownership of the main context.
368 ///
369 /// Ownership will automatically be released again once the returned acquire guard is dropped.
370 ///
371 /// This will fail if the main context is owned already by another thread.
372 // rustdoc-stripper-ignore-next-stop
373 /// Tries to become the owner of the specified context.
374 ///
375 /// If some other thread is the owner of the context,
376 /// returns false immediately. Ownership is properly
377 /// recursive: the owner can require ownership again
378 /// and will release ownership when [`release()`][Self::release()]
379 /// is called as many times as [`acquire()`][Self::acquire()].
380 ///
381 /// You must be the owner of a context before you
382 /// can call [`prepare()`][Self::prepare()], `GLib::MainContext::query()`,
383 /// `GLib::MainContext::check()`, [`dispatch()`][Self::dispatch()],
384 /// [`release()`][Self::release()].
385 ///
386 /// Since 2.76 @self can be `NULL` to use the global-default
387 /// main context.
388 ///
389 /// # Returns
390 ///
391 /// true if this thread is now the owner of @self, false otherwise
392 // rustdoc-stripper-ignore-next-stop
393 /// Tries to become the owner of the specified context.
394 ///
395 /// If some other thread is the owner of the context,
396 /// returns false immediately. Ownership is properly
397 /// recursive: the owner can require ownership again
398 /// and will release ownership when [`release()`][Self::release()]
399 /// is called as many times as [`acquire()`][Self::acquire()].
400 ///
401 /// You must be the owner of a context before you
402 /// can call [`prepare()`][Self::prepare()], `GLib::MainContext::query()`,
403 /// `GLib::MainContext::check()`, [`dispatch()`][Self::dispatch()],
404 /// [`release()`][Self::release()].
405 ///
406 /// Since 2.76 @self can be `NULL` to use the global-default
407 /// main context.
408 ///
409 /// # Returns
410 ///
411 /// true if this thread is now the owner of @self, false otherwise
412 #[doc(alias = "g_main_context_acquire")]
413 pub fn acquire(&self) -> Result<MainContextAcquireGuard<'_>, crate::BoolError> {
414 unsafe {
415 let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
416 if ret {
417 Ok(MainContextAcquireGuard(self))
418 } else {
419 Err(bool_error!(
420 "Failed to acquire ownership of main context, already acquired by another thread"
421 ))
422 }
423 }
424 }
425}
426
427#[must_use = "if unused the main context will be released immediately"]
428pub struct MainContextAcquireGuard<'a>(&'a MainContext);
429
430impl Drop for MainContextAcquireGuard<'_> {
431 #[doc(alias = "g_main_context_release")]
432 #[inline]
433 fn drop(&mut self) {
434 unsafe {
435 ffi::g_main_context_release(self.0.to_glib_none().0);
436 }
437 }
438}
439
440struct ThreadDefaultContext<'a>(&'a MainContext);
441
442impl ThreadDefaultContext<'_> {
443 fn new(ctx: &MainContext) -> ThreadDefaultContext<'_> {
444 unsafe {
445 ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
446 }
447 ThreadDefaultContext(ctx)
448 }
449}
450
451impl Drop for ThreadDefaultContext<'_> {
452 #[inline]
453 fn drop(&mut self) {
454 unsafe {
455 ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
456 }
457 }
458}
459
460#[cfg(test)]
461mod tests {
462 use std::{panic, ptr, thread};
463
464 use super::*;
465
466 #[test]
467 fn test_invoke() {
468 let c = MainContext::new();
469 let l = crate::MainLoop::new(Some(&c), false);
470
471 let l_clone = l.clone();
472 let join_handle = thread::spawn(move || {
473 c.invoke(move || l_clone.quit());
474 });
475
476 l.run();
477
478 join_handle.join().unwrap();
479 }
480
481 fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
482 ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
483 }
484
485 #[test]
486 fn test_with_thread_default() {
487 let a = MainContext::new();
488 let b = MainContext::new();
489
490 assert!(!is_same_context(&a, &b));
491
492 a.with_thread_default(|| {
493 let t = MainContext::thread_default().unwrap();
494 assert!(is_same_context(&a, &t));
495
496 b.with_thread_default(|| {
497 let t = MainContext::thread_default().unwrap();
498 assert!(is_same_context(&b, &t));
499 })
500 .unwrap();
501
502 let t = MainContext::thread_default().unwrap();
503 assert!(is_same_context(&a, &t));
504 })
505 .unwrap();
506 }
507
508 #[test]
509 fn test_with_thread_default_is_panic_safe() {
510 let a = MainContext::new();
511 let b = MainContext::new();
512
513 assert!(!is_same_context(&a, &b));
514
515 a.with_thread_default(|| {
516 let t = MainContext::thread_default().unwrap();
517 assert!(is_same_context(&a, &t));
518
519 let result = panic::catch_unwind(|| {
520 b.with_thread_default(|| {
521 panic!();
522 })
523 .unwrap();
524 });
525 assert!(result.is_err());
526
527 let t = MainContext::thread_default().unwrap();
528 assert!(is_same_context(&a, &t));
529 })
530 .unwrap();
531 }
532}