proxima_primitives/sync/
semaphore.rs1use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
30
31use event_listener::Event;
32use futures::FutureExt;
33
34#[derive(Debug)]
36pub struct Semaphore {
37 inner: async_lock::Semaphore,
38 closed: AtomicBool,
39 close_event: Event,
40 available: AtomicUsize,
41}
42
43impl Semaphore {
44 #[cfg(not(loom))]
54 #[must_use]
55 pub const fn new(permits: usize) -> Self {
56 Self {
57 inner: async_lock::Semaphore::new(permits),
58 closed: AtomicBool::new(false),
59 close_event: Event::new(),
60 available: AtomicUsize::new(permits),
61 }
62 }
63
64 #[cfg(loom)]
65 #[must_use]
66 pub fn new(permits: usize) -> Self {
67 Self {
68 inner: async_lock::Semaphore::new(permits),
69 closed: AtomicBool::new(false),
70 close_event: Event::new(),
71 available: AtomicUsize::new(permits),
72 }
73 }
74
75 pub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError> {
79 if self.is_closed() {
80 return Err(AcquireError(()));
81 }
82 match self.try_acquire() {
83 Ok(permit) => {
84 if self.is_closed() {
85 drop(permit);
86 return Err(AcquireError(()));
87 }
88 return Ok(permit);
89 }
90 Err(TryAcquireError::Closed) => return Err(AcquireError(())),
91 Err(TryAcquireError::NoPermits) => {}
92 }
93 let listener = self.close_event.listen();
95 if self.is_closed() {
96 return Err(AcquireError(()));
97 }
98 let acquire_future = self.inner.acquire().fuse();
99 let mut close_future = listener.fuse();
100 futures::pin_mut!(acquire_future);
101 futures::select_biased! {
102 _ = close_future => Err(AcquireError(())),
103 guard = acquire_future => {
104 self.available.fetch_sub(1, Ordering::Release);
105 Ok(SemaphorePermit { guard: Some(guard), owner: self })
106 }
107 }
108 }
109
110 pub fn try_acquire(&self) -> Result<SemaphorePermit<'_>, TryAcquireError> {
112 if self.is_closed() {
113 return Err(TryAcquireError::Closed);
114 }
115 match self.inner.try_acquire() {
116 Some(guard) => {
117 self.available.fetch_sub(1, Ordering::Release);
118 Ok(SemaphorePermit {
119 guard: Some(guard),
120 owner: self,
121 })
122 }
123 None => Err(TryAcquireError::NoPermits),
124 }
125 }
126
127 pub fn add_permits(&self, n: usize) {
129 self.inner.add_permits(n);
130 self.available.fetch_add(n, Ordering::Release);
131 }
132
133 #[must_use]
136 pub fn available_permits(&self) -> usize {
137 self.available.load(Ordering::Acquire)
138 }
139
140 pub fn close(&self) {
143 self.closed.store(true, Ordering::SeqCst);
144 self.close_event.notify(usize::MAX);
145 }
146
147 #[must_use]
149 pub fn is_closed(&self) -> bool {
150 self.closed.load(Ordering::SeqCst)
151 }
152}
153
154#[derive(Debug)]
156pub struct SemaphorePermit<'a> {
157 guard: Option<async_lock::SemaphoreGuard<'a>>,
158 owner: &'a Semaphore,
159}
160
161impl SemaphorePermit<'_> {
162 pub fn forget(mut self) {
166 if let Some(guard) = self.guard.take() {
167 guard.forget();
168 }
169 }
170}
171
172impl Drop for SemaphorePermit<'_> {
173 fn drop(&mut self) {
174 if self.guard.is_some() {
175 self.owner.available.fetch_add(1, Ordering::Release);
176 }
177 }
178}
179
180#[derive(Debug, thiserror::Error)]
182#[error("semaphore closed")]
183pub struct AcquireError(());
184
185#[derive(Debug, thiserror::Error)]
187pub enum TryAcquireError {
188 #[error("semaphore closed")]
190 Closed,
191 #[error("no permits available")]
193 NoPermits,
194}
195
196pub use async_lock::{SemaphoreGuard, SemaphoreGuardArc};
200
201#[cfg(all(test, not(loom)))]
206mod tests {
207 #![allow(
208 clippy::unwrap_used,
209 clippy::expect_used,
210 clippy::field_reassign_with_default,
211 clippy::type_complexity,
212 clippy::useless_vec,
213 clippy::needless_range_loop,
214 clippy::default_constructed_unit_structs
215 )]
216 use alloc::sync::Arc;
217 use alloc::task::Wake;
218 use core::future::Future;
219 use core::task::{Context, Poll, Waker};
220
221 use super::*;
222
223 #[derive(Default)]
224 struct WakeCounter(AtomicUsize);
225
226 impl Wake for WakeCounter {
227 fn wake(self: Arc<Self>) {
228 self.0.fetch_add(1, Ordering::Relaxed);
229 }
230
231 fn wake_by_ref(self: &Arc<Self>) {
232 self.0.fetch_add(1, Ordering::Relaxed);
233 }
234 }
235
236 #[proxima::test]
237 async fn acquire_returns_ok_when_permit_available() {
238 let semaphore = Semaphore::new(2);
239 assert_eq!(semaphore.available_permits(), 2);
240 let permit = semaphore.acquire().await.expect("permit");
241 assert_eq!(semaphore.available_permits(), 1);
242 drop(permit);
243 assert_eq!(semaphore.available_permits(), 2);
244 }
245
246 #[proxima::test]
247 async fn try_acquire_returns_no_permits_when_exhausted() {
248 let semaphore = Semaphore::new(1);
249 let _held = semaphore.acquire().await.expect("first permit");
250 let result = semaphore.try_acquire();
251 assert!(matches!(result, Err(TryAcquireError::NoPermits)));
252 }
253
254 #[proxima::test]
255 async fn close_then_acquire_returns_acquire_error() {
256 let semaphore = Semaphore::new(1);
257 semaphore.close();
258 assert!(semaphore.is_closed());
259 let result = semaphore.acquire().await;
260 assert!(matches!(result, Err(AcquireError(_))));
261 }
262
263 #[proxima::test]
264 async fn close_wakes_pending_acquires() {
265 let semaphore = Semaphore::new(1);
266 let held = semaphore.acquire().await.expect("first permit");
267 let wake_counter = Arc::new(WakeCounter::default());
268 let waker = Waker::from(wake_counter.clone());
269 let mut context = Context::from_waker(&waker);
270 let mut pending_acquire = core::pin::pin!(semaphore.acquire());
271 assert!(matches!(
272 Future::poll(pending_acquire.as_mut(), &mut context),
273 Poll::Pending
274 ));
275 assert_eq!(wake_counter.0.load(Ordering::Relaxed), 0);
276 semaphore.close();
277 assert_eq!(wake_counter.0.load(Ordering::Relaxed), 1);
278 assert!(matches!(
279 Future::poll(pending_acquire.as_mut(), &mut context),
280 Poll::Ready(Err(_))
281 ));
282 wake_counter.0.store(0, Ordering::Relaxed);
283 drop(held);
284 assert_eq!(wake_counter.0.load(Ordering::Relaxed), 0);
285 }
286
287 #[proxima::test]
288 async fn release_wakes_one_pending_acquire() {
289 let semaphore = Semaphore::new(1);
290 let held = semaphore.acquire().await.expect("first permit");
291 let wake_counter = Arc::new(WakeCounter::default());
292 let waker = Waker::from(wake_counter.clone());
293 let mut context = Context::from_waker(&waker);
294 let mut pending_acquire = core::pin::pin!(semaphore.acquire());
295 assert!(matches!(
296 Future::poll(pending_acquire.as_mut(), &mut context),
297 Poll::Pending
298 ));
299 assert_eq!(wake_counter.0.load(Ordering::Relaxed), 0);
300 drop(held);
301 assert_eq!(wake_counter.0.load(Ordering::Relaxed), 1);
302 assert!(matches!(
303 Future::poll(pending_acquire.as_mut(), &mut context),
304 Poll::Ready(Ok(_))
305 ));
306 assert_eq!(semaphore.available_permits(), 1);
307 }
308
309 #[proxima::test]
310 async fn add_permits_increases_available_count() {
311 let semaphore = Semaphore::new(1);
312 semaphore.add_permits(3);
313 assert_eq!(semaphore.available_permits(), 4);
314 }
315}