This commit is contained in:
jD91mZM2
2019-07-07 12:17:48 +02:00
parent 35c1d5210c
commit 43ff8801bc
9 changed files with 70 additions and 53 deletions
+19 -13
View File
@@ -6,8 +6,8 @@ pub use self::once::Once;
use core::cell::UnsafeCell;
use core::ops::Deref;
use core::sync::atomic::AtomicI32 as AtomicInt;
use core::sync::atomic;
use core::sync::atomic::AtomicI32 as AtomicInt;
use platform::types::*;
use platform::{Pal, Sys};
@@ -18,28 +18,36 @@ const FUTEX_WAKE: c_int = 1;
enum AttemptStatus {
Desired,
Waiting,
Other
Other,
}
/// Convenient wrapper around the "futex" system call for
/// synchronization implementations
struct AtomicLock {
atomic: UnsafeCell<AtomicInt>
atomic: UnsafeCell<AtomicInt>,
}
impl AtomicLock {
pub const fn new(value: c_int) -> Self {
Self {
atomic: UnsafeCell::new(AtomicInt::new(value))
atomic: UnsafeCell::new(AtomicInt::new(value)),
}
}
pub fn notify_one(&self) {
Sys::futex(unsafe { &mut *self.atomic.get() }.get_mut(), FUTEX_WAKE, 1);
}
pub fn notify_all(&self) {
Sys::futex(unsafe { &mut *self.atomic.get() }.get_mut(), FUTEX_WAKE, c_int::max_value());
Sys::futex(
unsafe { &mut *self.atomic.get() }.get_mut(),
FUTEX_WAKE,
c_int::max_value(),
);
}
pub fn wait_if(&self, value: c_int) {
Sys::futex(unsafe { &mut *self.atomic.get() }.get_mut(), FUTEX_WAIT, value);
Sys::futex(
unsafe { &mut *self.atomic.get() }.get_mut(),
FUTEX_WAIT,
value,
);
}
/// A general way to efficiently wait for what might be a long time, using two closures:
///
@@ -63,7 +71,7 @@ impl AtomicLock {
pub fn wait_until<F1, F2>(&self, attempt: F1, mark_long: F2, long: c_int)
where
F1: Fn(&AtomicInt) -> AttemptStatus,
F2: Fn(&AtomicInt) -> AttemptStatus
F2: Fn(&AtomicInt) -> AttemptStatus,
{
// First, try spinning for really short durations
for _ in 0..999 {
@@ -84,9 +92,9 @@ impl AtomicLock {
}
if
// If we or somebody else already initiated a long
// wait, OR
previous == AttemptStatus::Waiting ||
// If we or somebody else already initiated a long
// wait, OR
previous == AttemptStatus::Waiting ||
// Otherwise, unless our attempt to initiate a long
// wait informed us that we might be done waiting
mark_long(self) != AttemptStatus::Desired
@@ -102,8 +110,6 @@ impl Deref for AtomicLock {
type Target = AtomicInt;
fn deref(&self) -> &Self::Target {
unsafe {
&*self.atomic.get()
}
unsafe { &*self.atomic.get() }
}
}
+11 -7
View File
@@ -5,8 +5,8 @@ use core::sync::atomic::Ordering::SeqCst;
use platform::types::*;
const UNLOCKED: c_int = 0;
const LOCKED: c_int = 1;
const WAITING: c_int = 2;
const LOCKED: c_int = 1;
const WAITING: c_int = 2;
pub struct Mutex<T> {
lock: AtomicLock,
@@ -40,7 +40,8 @@ impl<T> Mutex<T> {
/// on failure. You should probably not worry about this, it's used for
/// internal optimizations.
pub unsafe fn manual_try_lock(&self) -> Result<&mut T, c_int> {
self.lock.compare_exchange(UNLOCKED, LOCKED, SeqCst, SeqCst)
self.lock
.compare_exchange(UNLOCKED, LOCKED, SeqCst, SeqCst)
.map(|_| &mut *self.content.get())
}
/// Lock the mutex, returning the inner content. After doing this, it's
@@ -53,15 +54,18 @@ impl<T> Mutex<T> {
.map(|_| AttemptStatus::Desired)
.unwrap_or_else(|e| match e {
WAITING => AttemptStatus::Waiting,
_ => AttemptStatus::Other
_ => AttemptStatus::Other,
})
},
|lock| match lock.compare_exchange_weak(LOCKED, WAITING, SeqCst, SeqCst).unwrap_or_else(|e| e) {
|lock| match lock
.compare_exchange_weak(LOCKED, WAITING, SeqCst, SeqCst)
.unwrap_or_else(|e| e)
{
UNLOCKED => AttemptStatus::Desired,
WAITING => AttemptStatus::Waiting,
_ => AttemptStatus::Other
_ => AttemptStatus::Other,
},
WAITING
WAITING,
);
&mut *self.content.get()
}
+21 -16
View File
@@ -5,13 +5,13 @@ use core::sync::atomic::Ordering::SeqCst;
use platform::types::*;
const UNINITIALIZED: c_int = 0;
const INITIALIZING: c_int = 1;
const WAITING: c_int = 2;
const INITIALIZED: c_int = 3;
const INITIALIZING: c_int = 1;
const WAITING: c_int = 2;
const INITIALIZED: c_int = 3;
pub struct Once<T> {
status: AtomicLock,
data: UnsafeCell<MaybeUninit<T>>
data: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T: Send> Send for Once<T> {}
unsafe impl<T: Send> Sync for Once<T> {}
@@ -19,13 +19,17 @@ impl<T> Once<T> {
pub const fn new() -> Self {
Self {
status: AtomicLock::new(UNINITIALIZED),
data: UnsafeCell::new(MaybeUninit::uninit())
data: UnsafeCell::new(MaybeUninit::uninit()),
}
}
pub fn call_once<F>(&self, f: F) -> &mut T
where F: FnOnce() -> T
where
F: FnOnce() -> T,
{
match self.status.compare_and_swap(UNINITIALIZED, INITIALIZING, SeqCst) {
match self
.status
.compare_and_swap(UNINITIALIZED, INITIALIZING, SeqCst)
{
UNINITIALIZED => {
// We now have a lock, let's initiate things!
let ret = unsafe { &mut *self.data.get() }.write(f());
@@ -35,28 +39,29 @@ impl<T> Once<T> {
// At least one thread is waiting on this to finish
self.status.notify_all();
}
},
}
INITIALIZING | WAITING => self.status.wait_until(
|lock| match lock.load(SeqCst) {
WAITING => AttemptStatus::Waiting,
INITIALIZED => AttemptStatus::Desired,
_ => AttemptStatus::Other
_ => AttemptStatus::Other,
},
|lock| match lock.compare_exchange_weak(INITIALIZING, WAITING, SeqCst, SeqCst).unwrap_or_else(|e| e) {
|lock| match lock
.compare_exchange_weak(INITIALIZING, WAITING, SeqCst, SeqCst)
.unwrap_or_else(|e| e)
{
WAITING => AttemptStatus::Waiting,
INITIALIZED => AttemptStatus::Desired,
_ => AttemptStatus::Other
_ => AttemptStatus::Other,
},
WAITING
WAITING,
),
INITIALIZED => (),
_ => unreachable!("invalid state for Once<T>")
_ => unreachable!("invalid state for Once<T>"),
}
// At this point the data must be initialized!
unsafe {
&mut *(&mut *self.data.get()).as_mut_ptr()
}
unsafe { &mut *(&mut *self.data.get()).as_mut_ptr() }
}
}
impl<T> Default for Once<T> {