1use glib_sys::{self, gboolean, gpointer};
6use source::Priority;
7use std::mem;
8use translate::*;
9use MainContext;
10use Source;
11use SourceId;
12
13impl MainContext {
14 pub fn prepare(&self) -> (bool, i32) {
15 unsafe {
16 let mut priority = mem::uninitialized();
17
18 let res = from_glib(glib_sys::g_main_context_prepare(
19 self.to_glib_none().0,
20 &mut priority,
21 ));
22
23 (res, priority)
24 }
25 }
26
27 pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
28 unsafe {
29 from_glib_none(glib_sys::g_main_context_find_source_by_id(
30 self.to_glib_none().0,
31 source_id.to_glib(),
32 ))
33 }
34 }
35
36 pub fn invoke<F>(&self, func: F)
38 where
39 F: FnOnce() + Send + 'static,
40 {
41 self.invoke_with_priority(::PRIORITY_DEFAULT_IDLE, func);
42 }
43
44 pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
46 where
47 F: FnOnce() + Send + 'static,
48 {
49 unsafe {
50 self.invoke_unsafe(priority, func);
51 }
52 }
53
54 pub fn invoke_local<F>(&self, func: F)
62 where
63 F: FnOnce() + 'static,
64 {
65 self.invoke_local_with_priority(::PRIORITY_DEFAULT_IDLE, func);
66 }
67
68 pub fn invoke_local_with_priority<F>(&self, priority: Priority, func: F)
76 where
77 F: FnOnce() + 'static,
78 {
79 unsafe {
80 assert!(self.is_owner());
81 self.invoke_unsafe(priority, func);
82 }
83 }
84
85 unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
86 where
87 F: FnOnce() + 'static,
88 {
89 unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
90 let func: &mut Option<F> = &mut *(func as *mut Option<F>);
91 let func = func
92 .take()
93 .expect("MainContext::invoke() closure called multiple times");
94 func();
95 glib_sys::G_SOURCE_REMOVE
96 }
97 unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
98 Box::<Option<F>>::from_raw(ptr as *mut _);
99 }
100 let func = Box::into_raw(Box::new(Some(func)));
101 glib_sys::g_main_context_invoke_full(
102 self.to_glib_none().0,
103 priority.to_glib(),
104 Some(trampoline::<F>),
105 func as gpointer,
106 Some(destroy_closure::<F>),
107 )
108 }
109
110 pub fn with_thread_default<R, F: Sized>(&self, func: F) -> R
120 where
121 F: FnOnce() -> R,
122 {
123 let _thread_default = ThreadDefaultContext::new(self);
124 func()
125 }
126}
127
128struct ThreadDefaultContext<'a>(&'a MainContext);
129
130impl<'a> ThreadDefaultContext<'a> {
131 fn new(ctx: &MainContext) -> ThreadDefaultContext {
132 ctx.push_thread_default();
133 ThreadDefaultContext(ctx)
134 }
135}
136
137impl<'a> Drop for ThreadDefaultContext<'a> {
138 fn drop(&mut self) {
139 self.0.pop_thread_default();
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use std::panic;
147 use std::ptr;
148 use std::thread;
149
150 #[test]
151 fn test_invoke() {
152 let c = MainContext::new();
153 let l = ::MainLoop::new(Some(&c), false);
154
155 let l_clone = l.clone();
156 thread::spawn(move || {
157 c.invoke(move || l_clone.quit());
158 });
159
160 l.run();
161 }
162
163 fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
164 ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
165 }
166
167 #[test]
168 fn test_with_thread_default() {
169 let a = MainContext::new();
170 let b = MainContext::new();
171
172 assert!(!is_same_context(&a, &b));
173
174 a.with_thread_default(|| {
175 let t = MainContext::get_thread_default().unwrap();
176 assert!(is_same_context(&a, &t));
177
178 &b.with_thread_default(|| {
179 let t = MainContext::get_thread_default().unwrap();
180 assert!(is_same_context(&b, &t));
181 });
182
183 let t = MainContext::get_thread_default().unwrap();
184 assert!(is_same_context(&a, &t));
185 });
186 }
187
188 #[test]
189 fn test_with_thread_default_is_panic_safe() {
190 let a = MainContext::new();
191 let b = MainContext::new();
192
193 assert!(!is_same_context(&a, &b));
194
195 a.with_thread_default(|| {
196 let t = MainContext::get_thread_default().unwrap();
197 assert!(is_same_context(&a, &t));
198
199 let result = panic::catch_unwind(|| {
200 &b.with_thread_default(|| {
201 panic!();
202 });
203 });
204 assert!(result.is_err());
205
206 let t = MainContext::get_thread_default().unwrap();
207 assert!(is_same_context(&a, &t));
208 });
209 }
210}