Format
This commit is contained in:
+19
-13
@@ -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
@@ -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
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user