]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
62682a34 | 11 | #![unstable(feature = "semaphore", |
85aaf69f | 12 | reason = "the interaction between semaphores and the acquisition/release \ |
e9174d1e SL |
13 | of resources is currently unclear", |
14 | issue = "27798")] | |
9cc50fc6 | 15 | #![allow(deprecated)] |
1a4d82fc JJ |
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 | /// | |
c34b1796 | 27 | /// # Examples |
1a4d82fc JJ |
28 | /// |
29 | /// ``` | |
c1a9b12d SL |
30 | /// #![feature(semaphore)] |
31 | /// | |
1a4d82fc JJ |
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 | /// ``` | |
7453a54e SL |
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")] | |
1a4d82fc | 56 | pub struct Semaphore { |
c34b1796 | 57 | lock: Mutex<isize>, |
1a4d82fc JJ |
58 | cvar: Condvar, |
59 | } | |
60 | ||
61 | /// An RAII guard which will release a resource acquired from a semaphore when | |
62 | /// dropped. | |
7453a54e SL |
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")] | |
1a4d82fc JJ |
70 | pub struct SemaphoreGuard<'a> { |
71 | sem: &'a Semaphore, | |
72 | } | |
73 | ||
7453a54e SL |
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")] | |
1a4d82fc JJ |
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. | |
c34b1796 | 87 | pub fn new(count: isize) -> Semaphore { |
1a4d82fc JJ |
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 | ||
85aaf69f | 127 | #[stable(feature = "rust1", since = "1.0.0")] |
1a4d82fc JJ |
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; | |
85aaf69f | 141 | use thread; |
1a4d82fc JJ |
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(); | |
85aaf69f | 161 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
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(); | |
85aaf69f | 173 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
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(); | |
85aaf69f | 184 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
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(); | |
85aaf69f | 200 | let _t = thread::spawn(move|| { |
1a4d82fc JJ |
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(); | |
85aaf69f | 217 | thread::spawn(move|| { |
1a4d82fc JJ |
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 | } |