Skip to main content

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}