#![deny(missing_docs)]
extern crate libc;
use std::io::Error as IoError;
#[cfg(target_os = "linux")]
use std::os::unix::thread::JoinHandleExt;
#[cfg(target_os = "linux")]
use std::ptr;
use std::thread;
use std::time::Duration;
#[cfg(target_os = "linux")]
use std::time::{self, SystemTime};
#[cfg(target_os = "linux")]
extern "C" {
fn pthread_tryjoin_np(thread: libc::pthread_t, retval: *mut *mut libc::c_void) -> libc::c_int;
fn pthread_timedjoin_np(
thread: libc::pthread_t,
retval: *mut *mut libc::c_void,
abstime: *const libc::timespec,
) -> libc::c_int;
}
pub trait TryJoinHandle {
fn try_join(&self) -> Result<(), IoError>;
fn try_timed_join(&self, wait: Duration) -> Result<(), IoError>;
}
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
impl<T> TryJoinHandle for thread::JoinHandle<T> {
fn try_join(&self) -> Result<(), IoError> {
unsafe {
let thread = self.as_pthread_t();
match pthread_tryjoin_np(thread, ptr::null_mut()) {
0 => Ok(()),
err => Err(IoError::from_raw_os_error(err)),
}
}
}
fn try_timed_join(&self, wait: Duration) -> Result<(), IoError> {
unsafe {
let thread = self.as_pthread_t();
let now = SystemTime::now();
let future = now + wait;
let total = future
.duration_since(time::UNIX_EPOCH)
.expect("Can't get time offset");
let abstime = libc::timespec {
tv_sec: total.as_secs() as i64,
tv_nsec: total.subsec_nanos() as i64,
};
match pthread_timedjoin_np(thread, ptr::null_mut(), &abstime as *const libc::timespec) {
0 => Ok(()),
err => Err(IoError::from_raw_os_error(err)),
}
}
}
}
#[cfg(all(target_os = "linux", any(target_arch = "arm", target_arch = "aarch64")))]
impl<T> TryJoinHandle for thread::JoinHandle<T> {
fn try_join(&self) -> Result<(), IoError> {
unsafe {
let thread = self.as_pthread_t();
match pthread_tryjoin_np(thread, ptr::null_mut()) {
0 => Ok(()),
err @ _ => Err(IoError::from_raw_os_error(err)),
}
}
}
fn try_timed_join(&self, wait: Duration) -> Result<(), IoError> {
unsafe {
let thread = self.as_pthread_t();
let now = SystemTime::now();
let future = now + wait;
let total = future
.duration_since(time::UNIX_EPOCH)
.expect("Can't get time offset");
#[cfg(target_arch = "arm")]
let abstime = libc::timespec {
tv_sec: total.as_secs() as i32,
tv_nsec: total.subsec_nanos() as i32,
};
#[cfg(target_arch = "aarch64")]
let abstime = libc::timespec {
tv_sec: total.as_secs() as i64,
tv_nsec: total.subsec_nanos() as i64,
};
match pthread_timedjoin_np(thread, ptr::null_mut(), &abstime as *const libc::timespec) {
0 => Ok(()),
err @ _ => Err(IoError::from_raw_os_error(err)),
}
}
}
}
#[cfg(not(target_os = "linux"))]
impl<T> TryJoinHandle for thread::JoinHandle<T> {
fn try_join(&self) -> Result<(), IoError> {
Err(IoError::from_raw_os_error(2))
}
fn try_timed_join(&self, _wait: Duration) -> Result<(), IoError> {
Err(IoError::from_raw_os_error(2))
}
}
#[cfg(all(test, target_os = "linux"))]
mod test {
use super::*;
use std::thread;
use std::time::Duration;
#[test]
fn basic_join() {
let t = thread::spawn(|| "ok");
assert_eq!("ok", t.join().unwrap());
}
#[test]
fn basic_try_join() {
let t = thread::spawn(|| "ok");
thread::sleep(Duration::from_millis(100));
assert!(t.try_join().is_ok());
}
#[test]
fn failing_try_join() {
let t = thread::spawn(|| {
thread::sleep(Duration::from_millis(500));
});
let err = t.try_join().unwrap_err();
assert_eq!(Some(16), err.raw_os_error());
thread::sleep(Duration::from_secs(1));
assert!(t.try_join().is_ok());
}
#[test]
fn basic_timed_join() {
let t = thread::spawn(|| "ok");
assert!(t.try_timed_join(Duration::from_secs(1)).is_ok());
}
#[test]
fn timed_join_timeout() {
let t = thread::spawn(|| {
thread::sleep(Duration::from_millis(500));
});
assert!(t.try_timed_join(Duration::from_millis(100)).is_err());
}
#[test]
fn timed_join_works() {
let t = thread::spawn(|| {
thread::sleep(Duration::from_millis(100));
});
assert!(t.try_timed_join(Duration::from_millis(500)).is_ok());
}
}