about summary refs log tree commit diff
path: root/src/libstd/sync/semaphore.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/sync/semaphore.rs')
-rw-r--r--src/libstd/sync/semaphore.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/libstd/sync/semaphore.rs b/src/libstd/sync/semaphore.rs
new file mode 100644
index 00000000000..03fb84c38d4
--- /dev/null
+++ b/src/libstd/sync/semaphore.rs
@@ -0,0 +1,195 @@
+// Copyright 2014 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 ops::Drop;
+use sync::{Mutex, Condvar};
+
+/// A counting, blocking, semaphore.
+///
+/// Semaphores are a form of atomic counter where access is only granted if the
+/// counter is a positive value. Each acquisition will block the calling thread
+/// until the counter is positive, and each release will increment the counter
+/// and unblock any threads if necessary.
+///
+/// # Example
+///
+/// ```
+/// use std::sync::Semaphore;
+///
+/// // Create a semaphore that represents 5 resources
+/// let sem = Semaphore::new(5);
+///
+/// // Acquire one of the resources
+/// sem.acquire();
+///
+/// // Acquire one of the resources for a limited period of time
+/// {
+///     let _guard = sem.access();
+///     // ...
+/// } // resources is released here
+///
+/// // Release our initially acquired resource
+/// sem.release();
+/// ```
+pub struct Semaphore {
+    lock: Mutex<int>,
+    cvar: Condvar,
+}
+
+/// An RAII guard which will release a resource acquired from a semaphore when
+/// dropped.
+pub struct SemaphoreGuard<'a> {
+    sem: &'a Semaphore,
+}
+
+impl Semaphore {
+    /// Creates a new semaphore with the initial count specified.
+    ///
+    /// The count specified can be thought of as a number of resources, and a
+    /// call to `acquire` or `access` will block until at least one resource is
+    /// available. It is valid to initialize a semaphore with a negative count.
+    pub fn new(count: int) -> Semaphore {
+        Semaphore {
+            lock: Mutex::new(count),
+            cvar: Condvar::new(),
+        }
+    }
+
+    /// Acquires a resource of this semaphore, blocking the current thread until
+    /// it can do so.
+    ///
+    /// This method will block until the internal count of the semaphore is at
+    /// least 1.
+    pub fn acquire(&self) {
+        let mut count = self.lock.lock();
+        while *count <= 0 {
+            self.cvar.wait(&count);
+        }
+        *count -= 1;
+    }
+
+    /// Release a resource from this semaphore.
+    ///
+    /// This will increment the number of resources in this semaphore by 1 and
+    /// will notify any pending waiters in `acquire` or `access` if necessary.
+    pub fn release(&self) {
+        *self.lock.lock() += 1;
+        self.cvar.notify_one();
+    }
+
+    /// Acquires a resource of this semaphore, returning an RAII guard to
+    /// release the semaphore when dropped.
+    ///
+    /// This function is semantically equivalent to an `acquire` followed by a
+    /// `release` when the guard returned is dropped.
+    pub fn access(&self) -> SemaphoreGuard {
+        self.acquire();
+        SemaphoreGuard { sem: self }
+    }
+}
+
+#[unsafe_destructor]
+impl<'a> Drop for SemaphoreGuard<'a> {
+    fn drop(&mut self) {
+        self.sem.release();
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use prelude::*;
+
+    use sync::Arc;
+    use super::Semaphore;
+
+    #[test]
+    fn test_sem_acquire_release() {
+        let s = Semaphore::new(1);
+        s.acquire();
+        s.release();
+        s.acquire();
+    }
+
+    #[test]
+    fn test_sem_basic() {
+        let s = Semaphore::new(1);
+        let _g = s.access();
+    }
+
+    #[test]
+    fn test_sem_as_mutex() {
+        let s = Arc::new(Semaphore::new(1));
+        let s2 = s.clone();
+        spawn(proc() {
+            let _g = s2.access();
+        });
+        let _g = s.access();
+    }
+
+    #[test]
+    fn test_sem_as_cvar() {
+        /* Child waits and parent signals */
+        let (tx, rx) = channel();
+        let s = Arc::new(Semaphore::new(0));
+        let s2 = s.clone();
+        spawn(proc() {
+            s2.acquire();
+            tx.send(());
+        });
+        s.release();
+        let _ = rx.recv();
+
+        /* Parent waits and child signals */
+        let (tx, rx) = channel();
+        let s = Arc::new(Semaphore::new(0));
+        let s2 = s.clone();
+        spawn(proc() {
+            s2.release();
+            let _ = rx.recv();
+        });
+        s.acquire();
+        tx.send(());
+    }
+
+    #[test]
+    fn test_sem_multi_resource() {
+        // Parent and child both get in the critical section at the same
+        // time, and shake hands.
+        let s = Arc::new(Semaphore::new(2));
+        let s2 = s.clone();
+        let (tx1, rx1) = channel();
+        let (tx2, rx2) = channel();
+        spawn(proc() {
+            let _g = s2.access();
+            let _ = rx2.recv();
+            tx1.send(());
+        });
+        let _g = s.access();
+        tx2.send(());
+        let _ = rx1.recv();
+    }
+
+    #[test]
+    fn test_sem_runtime_friendly_blocking() {
+        let s = Arc::new(Semaphore::new(1));
+        let s2 = s.clone();
+        let (tx, rx) = channel();
+        {
+            let _g = s.access();
+            spawn(proc() {
+                tx.send(());
+                drop(s2.access());
+                tx.send(());
+            });
+            rx.recv(); // wait for child to come alive
+        }
+        rx.recv(); // wait for child to be done
+    }
+}