use crate::source::Priority;
use crate::translate::*;
use crate::MainContext;
use crate::Source;
use crate::SourceId;
use ffi::{self, gboolean, gpointer};
use std::mem;
impl MainContext {
#[doc(alias = "g_main_context_prepare")]
pub fn prepare(&self) -> (bool, i32) {
unsafe {
let mut priority = mem::MaybeUninit::uninit();
let res = from_glib(ffi::g_main_context_prepare(
self.to_glib_none().0,
priority.as_mut_ptr(),
));
let priority = priority.assume_init();
(res, priority)
}
}
#[doc(alias = "g_main_context_find_source_by_id")]
pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
unsafe {
from_glib_none(ffi::g_main_context_find_source_by_id(
self.to_glib_none().0,
source_id.as_raw(),
))
}
}
#[doc(alias = "g_main_context_invoke")]
pub fn invoke<F>(&self, func: F)
where
F: FnOnce() + Send + 'static,
{
self.invoke_with_priority(crate::PRIORITY_DEFAULT_IDLE, func);
}
#[doc(alias = "g_main_context_invoke_full")]
pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
where
F: FnOnce() + Send + 'static,
{
unsafe {
self.invoke_unsafe(priority, func);
}
}
pub fn invoke_local<F>(&self, func: F)
where
F: FnOnce() + 'static,
{
self.invoke_local_with_priority(crate::PRIORITY_DEFAULT_IDLE, func);
}
#[allow(clippy::if_same_then_else)]
pub fn invoke_local_with_priority<F>(&self, _priority: Priority, func: F)
where
F: FnOnce() + 'static,
{
if self.is_owner() {
func();
} else if let Ok(_acquire) = self.acquire() {
func();
} else {
panic!("Must be called from a thread that owns the main context");
}
}
unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
where
F: FnOnce() + 'static,
{
unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
let func: &mut Option<F> = &mut *(func as *mut Option<F>);
let func = func
.take()
.expect("MainContext::invoke() closure called multiple times");
func();
ffi::G_SOURCE_REMOVE
}
unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
}
let func = Box::into_raw(Box::new(Some(func)));
ffi::g_main_context_invoke_full(
self.to_glib_none().0,
priority.into_glib(),
Some(trampoline::<F>),
func as gpointer,
Some(destroy_closure::<F>),
)
}
#[doc(alias = "g_main_context_push_thread_default")]
pub fn with_thread_default<R, F: Sized>(&self, func: F) -> Result<R, crate::BoolError>
where
F: FnOnce() -> R,
{
let _acquire = self.acquire()?;
let _thread_default = ThreadDefaultContext::new(self);
Ok(func())
}
#[doc(alias = "g_main_context_acquire")]
pub fn acquire(&self) -> Result<MainContextAcquireGuard, crate::BoolError> {
unsafe {
let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
if ret {
Ok(MainContextAcquireGuard(self))
} else {
Err(bool_error!("Failed to acquire ownership of main context, already acquired by another thread"))
}
}
}
}
#[must_use = "if unused the main context will be released immediately"]
pub struct MainContextAcquireGuard<'a>(&'a MainContext);
impl<'a> Drop for MainContextAcquireGuard<'a> {
#[doc(alias = "g_main_context_release")]
fn drop(&mut self) {
unsafe {
ffi::g_main_context_release(self.0.to_glib_none().0);
}
}
}
struct ThreadDefaultContext<'a>(&'a MainContext);
impl<'a> ThreadDefaultContext<'a> {
fn new(ctx: &MainContext) -> ThreadDefaultContext {
unsafe {
ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
}
ThreadDefaultContext(ctx)
}
}
impl<'a> Drop for ThreadDefaultContext<'a> {
fn drop(&mut self) {
unsafe {
ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::panic;
use std::ptr;
use std::thread;
#[test]
fn test_invoke() {
let c = MainContext::new();
let l = crate::MainLoop::new(Some(&c), false);
let l_clone = l.clone();
thread::spawn(move || {
c.invoke(move || l_clone.quit());
});
l.run();
}
fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
}
#[test]
fn test_with_thread_default() {
let a = MainContext::new();
let b = MainContext::new();
assert!(!is_same_context(&a, &b));
a.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
b.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&b, &t));
})
.unwrap();
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
})
.unwrap();
}
#[test]
fn test_with_thread_default_is_panic_safe() {
let a = MainContext::new();
let b = MainContext::new();
assert!(!is_same_context(&a, &b));
a.with_thread_default(|| {
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
let result = panic::catch_unwind(|| {
b.with_thread_default(|| {
panic!();
})
.unwrap();
});
assert!(result.is_err());
let t = MainContext::thread_default().unwrap();
assert!(is_same_context(&a, &t));
})
.unwrap();
}
}