ranked_semaphore/semaphore/permits.rs
1use std::fmt;
2use std::sync::atomic::Ordering;
3use std::sync::Arc;
4
5/// A permit that grants access to a resource protected by a semaphore.
6///
7/// This permit holds a reference to the semaphore and represents one or more
8/// permits that have been acquired. When the permit is dropped, the permits
9/// are automatically returned to the semaphore.
10///
11/// # Examples
12///
13/// ```rust
14/// use ranked_semaphore::RankedSemaphore;
15///
16/// # #[tokio::main]
17/// # async fn main() {
18/// let sem = RankedSemaphore::new_fifo(3);
19/// let permit = sem.acquire().await.unwrap();
20/// assert_eq!(permit.num_permits(), 1);
21/// // Permit is automatically returned when dropped
22/// # }
23/// ```
24pub struct RankedSemaphorePermit<'a> {
25 pub(crate) sem: &'a super::RankedSemaphore,
26 pub(crate) permits: u32,
27}
28
29/// An owned permit that grants access to a resource protected by a semaphore.
30///
31/// This permit owns a reference to the semaphore (via `Arc`) and represents
32/// one or more permits that have been acquired. When the permit is dropped,
33/// the permits are automatically returned to the semaphore.
34///
35/// # Examples
36///
37/// ```rust
38/// use ranked_semaphore::RankedSemaphore;
39/// use std::sync::Arc;
40///
41/// # #[tokio::main]
42/// # async fn main() {
43/// let sem = Arc::new(RankedSemaphore::new_fifo(3));
44/// let permit = sem.acquire_owned().await.unwrap();
45/// assert_eq!(permit.num_permits(), 1);
46/// // Permit is automatically returned when dropped
47/// # }
48/// ```
49pub struct OwnedRankedSemaphorePermit {
50 pub(crate) sem: Arc<super::RankedSemaphore>,
51 pub(crate) permits: u32,
52}
53
54impl<'a> RankedSemaphorePermit<'a> {
55 /// Forgets this permit without releasing it back to the semaphore.
56 ///
57 /// This effectively removes the permits from circulation permanently.
58 /// Use this method when you want to prevent the permits from being
59 /// returned to the semaphore when the permit is dropped.
60 ///
61 /// # Examples
62 ///
63 /// ```rust
64 /// use ranked_semaphore::RankedSemaphore;
65 ///
66 /// # #[tokio::main]
67 /// # async fn main() {
68 /// let sem = RankedSemaphore::new_fifo(3);
69 /// let permit = sem.acquire().await.unwrap();
70 ///
71 /// assert_eq!(sem.available_permits(), 2);
72 /// permit.forget(); // Permit is not returned to semaphore
73 /// assert_eq!(sem.available_permits(), 2);
74 /// # }
75 /// ```
76 pub fn forget(mut self) {
77 self.permits = 0;
78 }
79
80 /// Returns the number of permits held by this permit object.
81 ///
82 /// # Examples
83 ///
84 /// ```rust
85 /// use ranked_semaphore::RankedSemaphore;
86 ///
87 /// # #[tokio::main]
88 /// # async fn main() {
89 /// let sem = RankedSemaphore::new_fifo(5);
90 /// let permit = sem.acquire_many(3).await.unwrap();
91 /// assert_eq!(permit.num_permits(), 3);
92 /// # }
93 /// ```
94 pub fn num_permits(&self) -> usize {
95 self.permits as usize
96 }
97
98 /// Merges another permit into this one, combining their permit counts.
99 ///
100 /// Both permits must belong to the same semaphore instance. After merging,
101 /// the other permit becomes invalid (holds 0 permits) and this permit
102 /// holds the combined count.
103 ///
104 /// # Arguments
105 ///
106 /// * `other` - Another permit from the same semaphore to merge
107 ///
108 /// # Panics
109 ///
110 /// Panics if the permits belong to different semaphores.
111 ///
112 /// # Examples
113 ///
114 /// ```rust
115 /// use ranked_semaphore::RankedSemaphore;
116 ///
117 /// # #[tokio::main]
118 /// # async fn main() {
119 /// let sem = RankedSemaphore::new_fifo(5);
120 /// let mut permit1 = sem.acquire_many(2).await.unwrap();
121 /// let permit2 = sem.acquire_many(1).await.unwrap();
122 ///
123 /// permit1.merge(permit2);
124 /// assert_eq!(permit1.num_permits(), 3);
125 /// # }
126 /// ```
127 pub fn merge(&mut self, mut other: Self) {
128 if !std::ptr::eq(self.sem, other.sem) {
129 panic!("Cannot merge permits from different semaphores");
130 }
131 self.permits += other.permits;
132 // Prevent double drop
133 other.permits = 0;
134 }
135
136 /// Splits off a specified number of permits into a new permit.
137 ///
138 /// This reduces the current permit's count by `n` and returns a new
139 /// permit holding `n` permits. If there are insufficient permits,
140 /// returns `None`.
141 ///
142 /// # Arguments
143 ///
144 /// * `n` - The number of permits to split off
145 ///
146 /// # Returns
147 ///
148 /// * `Some(RankedSemaphorePermit)` - New permit holding `n` permits
149 /// * `None` - If this permit holds fewer than `n` permits
150 ///
151 /// # Examples
152 ///
153 /// ```rust
154 /// use ranked_semaphore::RankedSemaphore;
155 ///
156 /// # #[tokio::main]
157 /// # async fn main() {
158 /// let sem = RankedSemaphore::new_fifo(5);
159 /// let mut permit = sem.acquire_many(3).await.unwrap();
160 ///
161 /// let split_permit = permit.split(2).unwrap();
162 /// assert_eq!(permit.num_permits(), 1);
163 /// assert_eq!(split_permit.num_permits(), 2);
164 /// # }
165 /// ```
166 pub fn split(&mut self, n: u32) -> Option<Self> {
167 if n > self.permits {
168 return None;
169 }
170 self.permits -= n;
171 Some(Self {
172 sem: self.sem,
173 permits: n,
174 })
175 }
176}
177
178impl<'a> Drop for RankedSemaphorePermit<'a> {
179 fn drop(&mut self) {
180 if self.permits == 0 {
181 return;
182 }
183
184 // Try fast path first - most common case is no waiters
185 let permits_to_add = (self.permits as usize) << super::RankedSemaphore::PERMIT_SHIFT;
186 let waiters = self.sem.waiters.lock().unwrap();
187
188 if waiters.is_empty() {
189 // No waiters, just add permits back atomically after releasing lock
190 drop(waiters);
191 self.sem
192 .permits
193 .fetch_add(permits_to_add, Ordering::Release);
194 return;
195 }
196
197 // There are waiters, use add_permits_locked directly with the lock we already have
198 self.sem.add_permits_locked(self.permits as usize, waiters);
199 }
200}
201
202impl OwnedRankedSemaphorePermit {
203 /// Forgets this permit without releasing it back to the semaphore.
204 ///
205 /// This effectively removes the permits from circulation permanently.
206 /// Use this method when you want to prevent the permits from being
207 /// returned to the semaphore when the permit is dropped.
208 ///
209 /// # Examples
210 ///
211 /// ```rust
212 /// use ranked_semaphore::RankedSemaphore;
213 /// use std::sync::Arc;
214 ///
215 /// # #[tokio::main]
216 /// # async fn main() {
217 /// let sem = Arc::new(RankedSemaphore::new_fifo(3));
218 /// let permit = sem.clone().acquire_owned().await.unwrap();
219 ///
220 /// assert_eq!(sem.available_permits(), 2);
221 /// permit.forget(); // Permit is not returned to semaphore
222 /// assert_eq!(sem.available_permits(), 2);
223 /// # }
224 /// ```
225 pub fn forget(mut self) {
226 self.permits = 0;
227 }
228
229 /// Returns the number of permits held by this permit object.
230 ///
231 /// # Examples
232 ///
233 /// ```rust
234 /// use ranked_semaphore::RankedSemaphore;
235 /// use std::sync::Arc;
236 ///
237 /// # #[tokio::main]
238 /// # async fn main() {
239 /// let sem = Arc::new(RankedSemaphore::new_fifo(5));
240 /// let permit = sem.acquire_many_owned(3).await.unwrap();
241 /// assert_eq!(permit.num_permits(), 3);
242 /// # }
243 /// ```
244 pub fn num_permits(&self) -> usize {
245 self.permits as usize
246 }
247
248 /// Merges another permit into this one, combining their permit counts.
249 ///
250 /// Both permits must belong to the same semaphore instance. After merging,
251 /// the other permit becomes invalid (holds 0 permits) and this permit
252 /// holds the combined count.
253 ///
254 /// # Arguments
255 ///
256 /// * `other` - Another permit from the same semaphore to merge
257 ///
258 /// # Panics
259 ///
260 /// Panics if the permits belong to different semaphores.
261 ///
262 /// # Examples
263 ///
264 /// ```rust
265 /// use ranked_semaphore::RankedSemaphore;
266 /// use std::sync::Arc;
267 ///
268 /// # #[tokio::main]
269 /// # async fn main() {
270 /// let sem = Arc::new(RankedSemaphore::new_fifo(5));
271 /// let mut permit1 = sem.clone().acquire_many_owned(2).await.unwrap();
272 /// let permit2 = sem.acquire_many_owned(1).await.unwrap();
273 ///
274 /// permit1.merge(permit2);
275 /// assert_eq!(permit1.num_permits(), 3);
276 /// # }
277 /// ```
278 pub fn merge(&mut self, mut other: Self) {
279 if !Arc::ptr_eq(&self.sem, &other.sem) {
280 panic!("Cannot merge permits from different semaphores");
281 }
282 self.permits += other.permits;
283 // Prevent double drop
284 other.permits = 0;
285 }
286
287 /// Splits off a specified number of permits into a new owned permit.
288 ///
289 /// This reduces the current permit's count by `n` and returns a new
290 /// owned permit holding `n` permits. If there are insufficient permits,
291 /// returns `None`.
292 ///
293 /// # Arguments
294 ///
295 /// * `n` - The number of permits to split off
296 ///
297 /// # Returns
298 ///
299 /// * `Some(OwnedRankedSemaphorePermit)` - New permit holding `n` permits
300 /// * `None` - If this permit holds fewer than `n` permits or `n` doesn't fit in u32
301 ///
302 /// # Examples
303 ///
304 /// ```rust
305 /// use ranked_semaphore::RankedSemaphore;
306 /// use std::sync::Arc;
307 ///
308 /// # #[tokio::main]
309 /// # async fn main() {
310 /// let sem = Arc::new(RankedSemaphore::new_fifo(5));
311 /// let mut permit = sem.acquire_many_owned(3).await.unwrap();
312 ///
313 /// let split_permit = permit.split(2).unwrap();
314 /// assert_eq!(permit.num_permits(), 1);
315 /// assert_eq!(split_permit.num_permits(), 2);
316 /// # }
317 /// ```
318 pub fn split(&mut self, n: usize) -> Option<Self> {
319 let n = u32::try_from(n).ok()?;
320
321 if n > self.permits {
322 return None;
323 }
324
325 self.permits -= n;
326
327 Some(Self {
328 sem: self.sem.clone(),
329 permits: n,
330 })
331 }
332
333 /// Returns a reference to the semaphore from which this permit was acquired.
334 ///
335 /// # Examples
336 ///
337 /// ```rust
338 /// use ranked_semaphore::RankedSemaphore;
339 /// use std::sync::Arc;
340 ///
341 /// # #[tokio::main]
342 /// # async fn main() {
343 /// let sem = Arc::new(RankedSemaphore::new_fifo(3));
344 /// let permit = sem.acquire_owned().await.unwrap();
345 ///
346 /// let sem_ref = permit.semaphore();
347 /// assert_eq!(sem_ref.available_permits(), 2);
348 /// # }
349 /// ```
350 pub fn semaphore(&self) -> &Arc<super::RankedSemaphore> {
351 &self.sem
352 }
353}
354
355impl Drop for OwnedRankedSemaphorePermit {
356 fn drop(&mut self) {
357 if self.permits == 0 {
358 return;
359 }
360
361 // Try fast path first - most common case is no waiters
362 let permits_to_add = (self.permits as usize) << super::RankedSemaphore::PERMIT_SHIFT;
363 let waiters = self.sem.waiters.lock().unwrap();
364
365 if waiters.is_empty() {
366 // No waiters, just add permits back atomically after releasing lock
367 drop(waiters);
368 self.sem
369 .permits
370 .fetch_add(permits_to_add, Ordering::Release);
371 return;
372 }
373
374 // There are waiters, use add_permits_locked directly with the lock we already have
375 self.sem.add_permits_locked(self.permits as usize, waiters);
376 }
377}
378
379// Debug implementations
380impl<'a> fmt::Debug for RankedSemaphorePermit<'a> {
381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
382 f.debug_struct("RankedSemaphorePermit")
383 .field("permits", &self.permits)
384 .finish()
385 }
386}
387
388impl fmt::Debug for OwnedRankedSemaphorePermit {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 f.debug_struct("OwnedRankedSemaphorePermit")
391 .field("permits", &self.permits)
392 .finish()
393 }
394}