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}