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