Skip to content

Commit aa1ad82

Browse files
committed
Use a thread local variable for the X11 error
So in the situation that multiple X11 clients are handling errors from different threads they don't see and interfere with each other's errors. As mentioned in micahrj/raw-gl-context#15 (review).
1 parent ba442a4 commit aa1ad82

File tree

1 file changed

+28
-36
lines changed

1 file changed

+28
-36
lines changed

src/gl/x11/errors.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ use std::fmt::{Debug, Formatter};
33
use x11::xlib;
44

55
use std::panic::AssertUnwindSafe;
6-
use std::sync::atomic::{AtomicPtr, Ordering};
76
use std::sync::Mutex;
87

9-
static CURRENT_ERROR_PTR: AtomicPtr<Mutex<Option<xlib::XErrorEvent>>> =
10-
AtomicPtr::new(::std::ptr::null_mut());
8+
thread_local! {
9+
/// Used as part of [`XerrorHandler::handle()`]. When an X11 error occurs during this function,
10+
/// the error gets copied to this mutex after which the program is allowed to resume. The error
11+
/// can then be converted to a regular Rust Result value afterwards.
12+
static CURRENT_X11_ERROR: Mutex<Option<xlib::XErrorEvent>> = Mutex::new(None);
13+
}
1114

1215
/// A helper struct for safe X11 error handling
1316
pub struct XErrorHandler<'a> {
@@ -33,24 +36,15 @@ impl<'a> XErrorHandler<'a> {
3336
/// Sets up a temporary X11 error handler for the duration of the given closure, and allows
3437
/// that closure to check on the latest X11 error at any time
3538
pub fn handle<T, F: FnOnce(&mut XErrorHandler) -> T>(
36-
display: *mut xlib::Display,
37-
handler: F,
39+
display: *mut xlib::Display, handler: F,
3840
) -> T {
3941
unsafe extern "C" fn error_handler(
40-
_dpy: *mut xlib::Display,
41-
err: *mut xlib::XErrorEvent,
42+
_dpy: *mut xlib::Display, err: *mut xlib::XErrorEvent,
4243
) -> i32 {
4344
// SAFETY: the error pointer should be safe to copy
4445
let err = *err;
45-
// SAFETY: the ptr can only point to a valid instance or be null
46-
let mutex = CURRENT_ERROR_PTR.load(Ordering::SeqCst).as_ref();
47-
let mutex = if let Some(mutex) = mutex {
48-
mutex
49-
} else {
50-
return 2;
51-
};
52-
53-
match mutex.lock() {
46+
47+
CURRENT_X11_ERROR.with(|mutex| match mutex.lock() {
5448
Ok(mut current_error) => {
5549
*current_error = Some(err);
5650
0
@@ -62,33 +56,31 @@ impl<'a> XErrorHandler<'a> {
6256
);
6357
1
6458
}
65-
}
59+
})
6660
}
6761

6862
// Flush all possible previous errors
6963
unsafe {
7064
xlib::XSync(display, 0);
7165
}
7266

73-
let mut mutex = Mutex::new(None);
74-
CURRENT_ERROR_PTR.store(&mut mutex, Ordering::SeqCst);
75-
76-
let old_handler = unsafe { xlib::XSetErrorHandler(Some(error_handler)) };
77-
let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
78-
let mut h = XErrorHandler {
79-
display,
80-
mutex: &mutex,
81-
};
82-
handler(&mut h)
83-
}));
84-
// Whatever happened, restore old error handler
85-
unsafe { xlib::XSetErrorHandler(old_handler) };
86-
CURRENT_ERROR_PTR.store(::core::ptr::null_mut(), Ordering::SeqCst);
87-
88-
match panic_result {
89-
Ok(v) => v,
90-
Err(e) => std::panic::resume_unwind(e),
91-
}
67+
CURRENT_X11_ERROR.with(|mutex| {
68+
// Make sure to clear any errors from the last call to this function
69+
*mutex.lock().unwrap() = None;
70+
71+
let old_handler = unsafe { xlib::XSetErrorHandler(Some(error_handler)) };
72+
let panic_result = std::panic::catch_unwind(AssertUnwindSafe(|| {
73+
let mut h = XErrorHandler { display, mutex: &mutex };
74+
handler(&mut h)
75+
}));
76+
// Whatever happened, restore old error handler
77+
unsafe { xlib::XSetErrorHandler(old_handler) };
78+
79+
match panic_result {
80+
Ok(v) => v,
81+
Err(e) => std::panic::resume_unwind(e),
82+
}
83+
})
9284
}
9385
}
9486

0 commit comments

Comments
 (0)