Skip to main content

qubit_budget/resource/budget/
managed_resource_permit.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines an RAII permit returned by a managed resource pool.
9
10use std::sync::Arc;
11
12use super::internal::ManagedResourcePoolInner;
13use crate::resource::ResourceQuantity;
14
15/// Owns acquired capacity and returns it to its pool when dropped.
16///
17/// A permit may move across threads when its resource and quantity types allow
18/// that movement. Calling [`Self::release`] returns capacity early; otherwise
19/// normal Drop, early returns, `?`, and panic unwinding all return it.
20///
21/// # Type Parameters
22///
23/// * `R` - Caller-defined resource value retained by the owning pool.
24/// * `Q` - Exact unsigned quantity owned by this permit.
25///
26/// # Examples
27///
28/// ```
29/// use qubit_budget::ManagedResourcePool;
30///
31/// let pool = ManagedResourcePool::new("connections", 1_u64);
32/// let permit = pool.try_acquire(1).expect("one connection should fit");
33/// assert_eq!(permit.amount(), 1);
34/// permit.release();
35/// assert_eq!(pool.available(), 1);
36/// ```
37#[must_use = "dropping the permit releases its acquired capacity"]
38#[derive(Debug)]
39pub struct ManagedResourcePermit<R, Q = u64>
40where
41    Q: ResourceQuantity,
42{
43    /// Shared state receiving this permit's quantity on release.
44    inner: Option<Arc<ManagedResourcePoolInner<R, Q>>>,
45    /// Quantity uniquely owned by this permit.
46    amount: Q,
47}
48
49impl<R, Q> ManagedResourcePermit<R, Q>
50where
51    Q: ResourceQuantity,
52{
53    /// Creates a permit for capacity already deducted from `inner`.
54    #[inline]
55    pub(super) fn new(inner: Arc<ManagedResourcePoolInner<R, Q>>, amount: Q) -> Self {
56        Self {
57            inner: Some(inner),
58            amount,
59        }
60    }
61
62    /// Returns the resource whose capacity this permit owns.
63    ///
64    /// # Panics
65    ///
66    /// Panics only if an internal invariant is violated and a live permit no
67    /// longer retains its owning pool.
68    #[must_use]
69    #[inline(always)]
70    pub fn resource(&self) -> &R {
71        self.inner
72            .as_ref()
73            .expect("a live managed resource permit always retains its pool")
74            .limit
75            .resource()
76    }
77
78    /// Returns the quantity owned by this permit.
79    #[must_use]
80    #[inline(always)]
81    pub const fn amount(&self) -> Q {
82        self.amount
83    }
84
85    /// Returns this permit's capacity before the end of its lexical scope.
86    ///
87    /// Consuming `self` prevents callers from releasing the same permit twice.
88    pub fn release(mut self) {
89        self.release_inner();
90    }
91
92    /// Returns capacity once and leaves Drop with no remaining work.
93    #[inline]
94    fn release_inner(&mut self) {
95        if let Some(inner) = self.inner.take() {
96            inner.release(self.amount);
97        }
98    }
99}
100
101impl<R, Q> Drop for ManagedResourcePermit<R, Q>
102where
103    Q: ResourceQuantity,
104{
105    /// Returns owned capacity without panicking during unwinding.
106    fn drop(&mut self) {
107        self.release_inner();
108    }
109}