Prevent EventLoop from getting initialized outside the main thread in cross-platform functions (#1186)

* Prevent EventLoop from getting initialized outside the main thread

This only applies to the cross-platform functions. We expose functions
to do this in a platform-specific manner, when available.

* Add CHANGELOG entry

* Formatting has changed since the latest stable update...

* Fix error spacing

* Unix: Prevent initializing EventLoop outside main thread

* Updates libc dependency to 0.2.64, as required by BSD platforms

* Update CHANGELOG.md for Linux implementation

* Finish sentence

* Consolidate documentation
This commit is contained in:
Osspial 2019-10-18 11:51:06 -04:00 committed by GitHub
parent af3ef52252
commit 28e3c35547
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 224 additions and 35 deletions

View file

@ -520,14 +520,22 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
impl<T: 'static> EventLoop<T> {
pub fn new() -> EventLoop<T> {
assert_is_main_thread("new_any_thread");
EventLoop::new_any_thread()
}
pub fn new_any_thread() -> EventLoop<T> {
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
match env_var.as_str() {
"x11" => {
// TODO: propagate
return EventLoop::new_x11().expect("Failed to initialize X11 backend");
return EventLoop::new_x11_any_thread()
.expect("Failed to initialize X11 backend");
}
"wayland" => {
return EventLoop::new_wayland().expect("Failed to initialize Wayland backend");
return EventLoop::new_wayland_any_thread()
.expect("Failed to initialize Wayland backend");
}
_ => panic!(
"Unknown environment variable value for {}, try one of `x11`,`wayland`",
@ -536,12 +544,12 @@ impl<T: 'static> EventLoop<T> {
}
}
let wayland_err = match EventLoop::new_wayland() {
let wayland_err = match EventLoop::new_wayland_any_thread() {
Ok(event_loop) => return event_loop,
Err(err) => err,
};
let x11_err = match EventLoop::new_x11() {
let x11_err = match EventLoop::new_x11_any_thread() {
Ok(event_loop) => return event_loop,
Err(err) => err,
};
@ -554,10 +562,22 @@ impl<T: 'static> EventLoop<T> {
}
pub fn new_wayland() -> Result<EventLoop<T>, ConnectError> {
assert_is_main_thread("new_wayland_any_thread");
EventLoop::new_wayland_any_thread()
}
pub fn new_wayland_any_thread() -> Result<EventLoop<T>, ConnectError> {
wayland::EventLoop::new().map(EventLoop::Wayland)
}
pub fn new_x11() -> Result<EventLoop<T>, XNotSupported> {
assert_is_main_thread("new_x11_any_thread");
EventLoop::new_x11_any_thread()
}
pub fn new_x11_any_thread() -> Result<EventLoop<T>, XNotSupported> {
X11_BACKEND
.lock()
.as_ref()
@ -672,3 +692,35 @@ fn sticky_exit_callback<T, F>(
// user callback
callback(evt, target, cf)
}
fn assert_is_main_thread(suggested_method: &str) {
if !is_main_thread() {
panic!(
"Initializing the event loop outside of the main thread is a significant \
cross-platform compatibility hazard. If you really, absolutely need to create an \
EventLoop on a different thread, please use the `EventLoopExtUnix::{}` function.",
suggested_method
);
}
}
#[cfg(target_os = "linux")]
fn is_main_thread() -> bool {
use libc::{c_long, getpid, syscall, SYS_gettid};
unsafe { syscall(SYS_gettid) == getpid() as c_long }
}
#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
fn is_main_thread() -> bool {
use libc::pthread_main_np;
unsafe { pthread_main_np() == 1 }
}
#[cfg(target_os = "netbsd")]
fn is_main_thread() -> bool {
use libc::_lwp_self;
unsafe { _lwp_self() == 1 }
}