]> git.lizzy.rs Git - rust.git/blob - src/libstd/sync/semaphore.rs
Auto merge of #27822 - arielb1:inline-round-take-2, r=Gankro
[rust.git] / src / libstd / sync / semaphore.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![unstable(feature = "semaphore",
12             reason = "the interaction between semaphores and the acquisition/release \
13                       of resources is currently unclear")]
14
15 use ops::Drop;
16 use sync::{Mutex, Condvar};
17
18 /// A counting, blocking, semaphore.
19 ///
20 /// Semaphores are a form of atomic counter where access is only granted if the
21 /// counter is a positive value. Each acquisition will block the calling thread
22 /// until the counter is positive, and each release will increment the counter
23 /// and unblock any threads if necessary.
24 ///
25 /// # Examples
26 ///
27 /// ```
28 /// #![feature(semaphore)]
29 ///
30 /// use std::sync::Semaphore;
31 ///
32 /// // Create a semaphore that represents 5 resources
33 /// let sem = Semaphore::new(5);
34 ///
35 /// // Acquire one of the resources
36 /// sem.acquire();
37 ///
38 /// // Acquire one of the resources for a limited period of time
39 /// {
40 ///     let _guard = sem.access();
41 ///     // ...
42 /// } // resources is released here
43 ///
44 /// // Release our initially acquired resource
45 /// sem.release();
46 /// ```
47 pub struct Semaphore {
48     lock: Mutex<isize>,
49     cvar: Condvar,
50 }
51
52 /// An RAII guard which will release a resource acquired from a semaphore when
53 /// dropped.
54 pub struct SemaphoreGuard<'a> {
55     sem: &'a Semaphore,
56 }
57
58 impl Semaphore {
59     /// Creates a new semaphore with the initial count specified.
60     ///
61     /// The count specified can be thought of as a number of resources, and a
62     /// call to `acquire` or `access` will block until at least one resource is
63     /// available. It is valid to initialize a semaphore with a negative count.
64     pub fn new(count: isize) -> Semaphore {
65         Semaphore {
66             lock: Mutex::new(count),
67             cvar: Condvar::new(),
68         }
69     }
70
71     /// Acquires a resource of this semaphore, blocking the current thread until
72     /// it can do so.
73     ///
74     /// This method will block until the internal count of the semaphore is at
75     /// least 1.
76     pub fn acquire(&self) {
77         let mut count = self.lock.lock().unwrap();
78         while *count <= 0 {
79             count = self.cvar.wait(count).unwrap();
80         }
81         *count -= 1;
82     }
83
84     /// Release a resource from this semaphore.
85     ///
86     /// This will increment the number of resources in this semaphore by 1 and
87     /// will notify any pending waiters in `acquire` or `access` if necessary.
88     pub fn release(&self) {
89         *self.lock.lock().unwrap() += 1;
90         self.cvar.notify_one();
91     }
92
93     /// Acquires a resource of this semaphore, returning an RAII guard to
94     /// release the semaphore when dropped.
95     ///
96     /// This function is semantically equivalent to an `acquire` followed by a
97     /// `release` when the guard returned is dropped.
98     pub fn access(&self) -> SemaphoreGuard {
99         self.acquire();
100         SemaphoreGuard { sem: self }
101     }
102 }
103
104 #[stable(feature = "rust1", since = "1.0.0")]
105 impl<'a> Drop for SemaphoreGuard<'a> {
106     fn drop(&mut self) {
107         self.sem.release();
108     }
109 }
110
111 #[cfg(test)]
112 mod tests {
113     use prelude::v1::*;
114
115     use sync::Arc;
116     use super::Semaphore;
117     use sync::mpsc::channel;
118     use thread;
119
120     #[test]
121     fn test_sem_acquire_release() {
122         let s = Semaphore::new(1);
123         s.acquire();
124         s.release();
125         s.acquire();
126     }
127
128     #[test]
129     fn test_sem_basic() {
130         let s = Semaphore::new(1);
131         let _g = s.access();
132     }
133
134     #[test]
135     fn test_sem_as_mutex() {
136         let s = Arc::new(Semaphore::new(1));
137         let s2 = s.clone();
138         let _t = thread::spawn(move|| {
139             let _g = s2.access();
140         });
141         let _g = s.access();
142     }
143
144     #[test]
145     fn test_sem_as_cvar() {
146         /* Child waits and parent signals */
147         let (tx, rx) = channel();
148         let s = Arc::new(Semaphore::new(0));
149         let s2 = s.clone();
150         let _t = thread::spawn(move|| {
151             s2.acquire();
152             tx.send(()).unwrap();
153         });
154         s.release();
155         let _ = rx.recv();
156
157         /* Parent waits and child signals */
158         let (tx, rx) = channel();
159         let s = Arc::new(Semaphore::new(0));
160         let s2 = s.clone();
161         let _t = thread::spawn(move|| {
162             s2.release();
163             let _ = rx.recv();
164         });
165         s.acquire();
166         tx.send(()).unwrap();
167     }
168
169     #[test]
170     fn test_sem_multi_resource() {
171         // Parent and child both get in the critical section at the same
172         // time, and shake hands.
173         let s = Arc::new(Semaphore::new(2));
174         let s2 = s.clone();
175         let (tx1, rx1) = channel();
176         let (tx2, rx2) = channel();
177         let _t = thread::spawn(move|| {
178             let _g = s2.access();
179             let _ = rx2.recv();
180             tx1.send(()).unwrap();
181         });
182         let _g = s.access();
183         tx2.send(()).unwrap();
184         rx1.recv().unwrap();
185     }
186
187     #[test]
188     fn test_sem_runtime_friendly_blocking() {
189         let s = Arc::new(Semaphore::new(1));
190         let s2 = s.clone();
191         let (tx, rx) = channel();
192         {
193             let _g = s.access();
194             thread::spawn(move|| {
195                 tx.send(()).unwrap();
196                 drop(s2.access());
197                 tx.send(()).unwrap();
198             });
199             rx.recv().unwrap(); // wait for child to come alive
200         }
201         rx.recv().unwrap(); // wait for child to be done
202     }
203 }