use std;
use std::thread::JoinHandle;
pub struct Joiner {
joiner: Option<JoinHandle<()>>,
}
pub type RaiiThreadJoiner = Joiner;
impl Joiner {
pub fn new(joiner: JoinHandle<()>) -> Joiner {
Joiner { joiner: Some(joiner) }
}
pub fn detach(mut self) {
let _ = unwrap!(self.joiner.take(),
"Programming error: please report this as a bug.");
}
}
impl Drop for Joiner {
fn drop(&mut self) {
if let Some(joiner) = self.joiner.take() {
unwrap!(joiner.join());
}
}
}
#[macro_export]
macro_rules! thread {
($thread_name:expr, $entry_point:expr) => {
unwrap!(::std::thread::Builder::new().name($thread_name.to_owned())
.spawn($entry_point))
}
}
pub fn named<S, F>(thread_name: S, func: F) -> Joiner
where S: Into<String>,
F: FnOnce() + Send + 'static
{
let thread_name: String = thread_name.into();
let join_handle_res = std::thread::Builder::new().name(thread_name)
.spawn(func);
Joiner::new(unwrap!(join_handle_res))
}
#[cfg(test)]
mod test {
use super::*;
use std::thread;
use std::time::{Duration, Instant};
#[test]
fn raii_thread_joiner() {
const SLEEP_DURATION_DAEMON: u64 = 150;
const SLEEP_DURATION_MANAGED: u64 = SLEEP_DURATION_DAEMON * 3;
{
let time_before = Instant::now();
{
named("JoinerTestDaemon", move || {
thread::sleep(Duration::from_millis(SLEEP_DURATION_DAEMON));
}).detach();
}
let diff = time_before.elapsed();
assert!(diff < Duration::from_millis(SLEEP_DURATION_DAEMON));
}
{
let time_before = Instant::now();
{
let _joiner = named("JoinerTestManaged", move || {
thread::sleep(Duration::from_millis(SLEEP_DURATION_MANAGED));
});
}
let diff = time_before.elapsed();
assert!(diff >= Duration::from_millis(SLEEP_DURATION_MANAGED));
}
}
}