about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2020-07-24 10:01:28 -0700
committerGitHub <noreply@github.com>2020-07-24 10:01:28 -0700
commitdfedb84462ed2281877ec10253e659155213e7c8 (patch)
treeda580062a33dd98d20dd91fc40537236fe4d1edb /src/libstd/sys
parentcfb6114b2a20e04e3212ed030bc68d23faf8048f (diff)
parent6925ebdd6051b3827c2978b8b285e49743306ccc (diff)
downloadrust-dfedb84462ed2281877ec10253e659155213e7c8.tar.gz
rust-dfedb84462ed2281877ec10253e659155213e7c8.zip
Rollup merge of #72954 - hermitcore:rwlock, r=dtolnay
revise RwLock for HermitCore

- current version is derived from the wasm implementation
- increasing the readability of `Condvar`
- simplify the interface to the libos
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/hermit/condvar.rs64
-rw-r--r--src/libstd/sys/hermit/rwlock.rs115
2 files changed, 139 insertions, 40 deletions
diff --git a/src/libstd/sys/hermit/condvar.rs b/src/libstd/sys/hermit/condvar.rs
index 132e579b3a5..52c8c3b17e8 100644
--- a/src/libstd/sys/hermit/condvar.rs
+++ b/src/libstd/sys/hermit/condvar.rs
@@ -1,60 +1,64 @@
-use crate::cmp;
+use crate::ffi::c_void;
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
 use crate::sys::hermit::abi;
 use crate::sys::mutex::Mutex;
 use crate::time::Duration;
 
+// The implementation is inspired by Andrew D. Birrell's paper
+// "Implementing Condition Variables with Semaphores"
+
 pub struct Condvar {
-    identifier: usize,
+    counter: AtomicUsize,
+    sem1: *const c_void,
+    sem2: *const c_void,
 }
 
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
 impl Condvar {
     pub const fn new() -> Condvar {
-        Condvar { identifier: 0 }
+        Condvar { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }
     }
 
     pub unsafe fn init(&mut self) {
-        let _ = abi::init_queue(self.id());
+        let _ = abi::sem_init(&mut self.sem1 as *mut *const c_void, 0);
+        let _ = abi::sem_init(&mut self.sem2 as *mut *const c_void, 0);
     }
 
     pub unsafe fn notify_one(&self) {
-        let _ = abi::notify(self.id(), 1);
+        if self.counter.load(SeqCst) > 0 {
+            self.counter.fetch_sub(1, SeqCst);
+            abi::sem_post(self.sem1);
+            abi::sem_timedwait(self.sem2, 0);
+        }
     }
 
-    #[inline]
     pub unsafe fn notify_all(&self) {
-        let _ = abi::notify(self.id(), -1 /* =all */);
+        let counter = self.counter.swap(0, SeqCst);
+        for _ in 0..counter {
+            abi::sem_post(self.sem1);
+        }
+        for _ in 0..counter {
+            abi::sem_timedwait(self.sem2, 0);
+        }
     }
 
     pub unsafe fn wait(&self, mutex: &Mutex) {
-        // add current task to the wait queue
-        let _ = abi::add_queue(self.id(), -1 /* no timeout */);
+        self.counter.fetch_add(1, SeqCst);
         mutex.unlock();
-        let _ = abi::wait(self.id());
+        abi::sem_timedwait(self.sem1, 0);
+        abi::sem_post(self.sem2);
         mutex.lock();
     }
 
-    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
-        let nanos = dur.as_nanos();
-        let nanos = cmp::min(i64::MAX as u128, nanos);
-
-        // add current task to the wait queue
-        let _ = abi::add_queue(self.id(), nanos as i64);
-
-        mutex.unlock();
-        // If the return value is !0 then a timeout happened, so we return
-        // `false` as we weren't actually notified.
-        let ret = abi::wait(self.id()) == 0;
-        mutex.lock();
-
-        ret
+    pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
+        panic!("wait_timeout not supported on hermit");
     }
 
     pub unsafe fn destroy(&self) {
-        let _ = abi::destroy_queue(self.id());
-    }
-
-    #[inline]
-    fn id(&self) -> usize {
-        &self.identifier as *const usize as usize
+        let _ = abi::sem_destroy(self.sem1);
+        let _ = abi::sem_destroy(self.sem2);
     }
 }
diff --git a/src/libstd/sys/hermit/rwlock.rs b/src/libstd/sys/hermit/rwlock.rs
index c19799af3c7..06442e925f4 100644
--- a/src/libstd/sys/hermit/rwlock.rs
+++ b/src/libstd/sys/hermit/rwlock.rs
@@ -1,49 +1,144 @@
-use super::mutex::Mutex;
+use crate::cell::UnsafeCell;
+use crate::sys::condvar::Condvar;
+use crate::sys::mutex::Mutex;
 
 pub struct RWLock {
-    mutex: Mutex,
+    lock: Mutex,
+    cond: Condvar,
+    state: UnsafeCell<State>,
+}
+
+enum State {
+    Unlocked,
+    Reading(usize),
+    Writing,
 }
 
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {}
 
+// This rwlock implementation is a relatively simple implementation which has a
+// condition variable for readers/writers as well as a mutex protecting the
+// internal state of the lock. A current downside of the implementation is that
+// unlocking the lock will notify *all* waiters rather than just readers or just
+// writers. This can cause lots of "thundering stampede" problems. While
+// hopefully correct this implementation is very likely to want to be changed in
+// the future.
+
 impl RWLock {
     pub const fn new() -> RWLock {
-        RWLock { mutex: Mutex::new() }
+        RWLock { lock: Mutex::new(), cond: Condvar::new(), state: UnsafeCell::new(State::Unlocked) }
     }
 
     #[inline]
     pub unsafe fn read(&self) {
-        self.mutex.lock();
+        self.lock.lock();
+        while !(*self.state.get()).inc_readers() {
+            self.cond.wait(&self.lock);
+        }
+        self.lock.unlock();
     }
 
     #[inline]
     pub unsafe fn try_read(&self) -> bool {
-        self.mutex.try_lock()
+        self.lock.lock();
+        let ok = (*self.state.get()).inc_readers();
+        self.lock.unlock();
+        return ok;
     }
 
     #[inline]
     pub unsafe fn write(&self) {
-        self.mutex.lock();
+        self.lock.lock();
+        while !(*self.state.get()).inc_writers() {
+            self.cond.wait(&self.lock);
+        }
+        self.lock.unlock();
     }
 
     #[inline]
     pub unsafe fn try_write(&self) -> bool {
-        self.mutex.try_lock()
+        self.lock.lock();
+        let ok = (*self.state.get()).inc_writers();
+        self.lock.unlock();
+        return ok;
     }
 
     #[inline]
     pub unsafe fn read_unlock(&self) {
-        self.mutex.unlock();
+        self.lock.lock();
+        let notify = (*self.state.get()).dec_readers();
+        self.lock.unlock();
+        if notify {
+            // FIXME: should only wake up one of these some of the time
+            self.cond.notify_all();
+        }
     }
 
     #[inline]
     pub unsafe fn write_unlock(&self) {
-        self.mutex.unlock();
+        self.lock.lock();
+        (*self.state.get()).dec_writers();
+        self.lock.unlock();
+        // FIXME: should only wake up one of these some of the time
+        self.cond.notify_all();
     }
 
     #[inline]
     pub unsafe fn destroy(&self) {
-        self.mutex.destroy();
+        self.lock.destroy();
+        self.cond.destroy();
+    }
+}
+
+impl State {
+    fn inc_readers(&mut self) -> bool {
+        match *self {
+            State::Unlocked => {
+                *self = State::Reading(1);
+                true
+            }
+            State::Reading(ref mut cnt) => {
+                *cnt += 1;
+                true
+            }
+            State::Writing => false,
+        }
+    }
+
+    fn inc_writers(&mut self) -> bool {
+        match *self {
+            State::Unlocked => {
+                *self = State::Writing;
+                true
+            }
+            State::Reading(_) | State::Writing => false,
+        }
+    }
+
+    fn dec_readers(&mut self) -> bool {
+        let zero = match *self {
+            State::Reading(ref mut cnt) => {
+                *cnt -= 1;
+                *cnt == 0
+            }
+            State::Unlocked | State::Writing => invalid(),
+        };
+        if zero {
+            *self = State::Unlocked;
+        }
+        zero
     }
+
+    fn dec_writers(&mut self) {
+        match *self {
+            State::Writing => {}
+            State::Unlocked | State::Reading(_) => invalid(),
+        }
+        *self = State::Unlocked;
+    }
+}
+
+fn invalid() -> ! {
+    panic!("inconsistent rwlock");
 }