blob: c05c837ade27498fef4e2d144e8a378535aa789b [file] [log] [blame]
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cell::UnsafeCell;
use mem;
use sync::atomic::{AtomicU32, Ordering};
use sys::cloudabi::abi;
use sys::mutex::{self, Mutex};
use sys::time::dur2intervals;
use time::Duration;
extern "C" {
#[thread_local]
static __pthread_thread_id: abi::tid;
}
pub struct Condvar {
condvar: UnsafeCell<AtomicU32>,
}
unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}
impl Condvar {
pub const fn new() -> Condvar {
Condvar {
condvar: UnsafeCell::new(AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0)),
}
}
pub unsafe fn init(&mut self) {}
pub unsafe fn notify_one(&self) {
let condvar = self.condvar.get();
if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
let ret = abi::condvar_signal(condvar as *mut abi::condvar, abi::scope::PRIVATE, 1);
assert_eq!(
ret,
abi::errno::SUCCESS,
"Failed to signal on condition variable"
);
}
}
pub unsafe fn notify_all(&self) {
let condvar = self.condvar.get();
if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 {
let ret = abi::condvar_signal(
condvar as *mut abi::condvar,
abi::scope::PRIVATE,
abi::nthreads::max_value(),
);
assert_eq!(
ret,
abi::errno::SUCCESS,
"Failed to broadcast on condition variable"
);
}
}
pub unsafe fn wait(&self, mutex: &Mutex) {
let mutex = mutex::raw(mutex);
assert_eq!(
(*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
__pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
"This lock is not write-locked by this thread"
);
// Call into the kernel to wait on the condition variable.
let condvar = self.condvar.get();
let subscription = abi::subscription {
type_: abi::eventtype::CONDVAR,
union: abi::subscription_union {
condvar: abi::subscription_condvar {
condvar: condvar as *mut abi::condvar,
condvar_scope: abi::scope::PRIVATE,
lock: mutex as *mut abi::lock,
lock_scope: abi::scope::PRIVATE,
},
},
..mem::zeroed()
};
let mut event: abi::event = mem::uninitialized();
let mut nevents: usize = mem::uninitialized();
let ret = abi::poll(&subscription, &mut event, 1, &mut nevents);
assert_eq!(
ret,
abi::errno::SUCCESS,
"Failed to wait on condition variable"
);
assert_eq!(
event.error,
abi::errno::SUCCESS,
"Failed to wait on condition variable"
);
}
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
let mutex = mutex::raw(mutex);
assert_eq!(
(*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0,
__pthread_thread_id.0 | abi::LOCK_WRLOCKED.0,
"This lock is not write-locked by this thread"
);
// Call into the kernel to wait on the condition variable.
let condvar = self.condvar.get();
let subscriptions = [
abi::subscription {
type_: abi::eventtype::CONDVAR,
union: abi::subscription_union {
condvar: abi::subscription_condvar {
condvar: condvar as *mut abi::condvar,
condvar_scope: abi::scope::PRIVATE,
lock: mutex as *mut abi::lock,
lock_scope: abi::scope::PRIVATE,
},
},
..mem::zeroed()
},
abi::subscription {
type_: abi::eventtype::CLOCK,
union: abi::subscription_union {
clock: abi::subscription_clock {
clock_id: abi::clockid::MONOTONIC,
timeout: dur2intervals(&dur),
..mem::zeroed()
},
},
..mem::zeroed()
},
];
let mut events: [abi::event; 2] = mem::uninitialized();
let mut nevents: usize = mem::uninitialized();
let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents);
assert_eq!(
ret,
abi::errno::SUCCESS,
"Failed to wait on condition variable"
);
for i in 0..nevents {
assert_eq!(
events[i].error,
abi::errno::SUCCESS,
"Failed to wait on condition variable"
);
if events[i].type_ == abi::eventtype::CONDVAR {
return true;
}
}
false
}
pub unsafe fn destroy(&self) {
let condvar = self.condvar.get();
assert_eq!(
(*condvar).load(Ordering::Relaxed),
abi::CONDVAR_HAS_NO_WAITERS.0,
"Attempted to destroy a condition variable with blocked threads"
);
}
}