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}