kvbm_logical/blocks/immutable.rs
1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! RAII guards for blocks in the **Registered** state.
5//!
6//! [`ImmutableBlock`] is the strong, cloneable handle that keeps a registered
7//! block alive. [`WeakBlock`] is its non-owning counterpart -- it does not
8//! prevent the block from being evicted, but can be cheaply upgraded back to
9//! an `ImmutableBlock` if the block is still present.
10
11use super::{
12 BlockId, BlockMetadata, BlockRegistrationHandle, RegisteredBlock, SequenceHash, UpgradeFn,
13};
14
15use crate::metrics::BlockPoolMetrics;
16use std::sync::{Arc, Weak};
17
18/// RAII guard for a block in the **Registered** state.
19///
20/// An `ImmutableBlock` is the primary handle through which callers interact
21/// with registered blocks. It is reference-counted (`Clone`-able) and each
22/// clone independently tracks the `inflight_immutable` metric gauge, so the
23/// gauge reflects the total number of outstanding references across the
24/// system.
25///
26/// # Obtaining an `ImmutableBlock`
27///
28/// - [`BlockManager::register_block`](crate::manager::BlockManager::register_block)
29/// -- registers a [`CompleteBlock`](super::CompleteBlock) and returns an
30/// `ImmutableBlock`.
31/// - [`BlockManager::match_blocks`](crate::manager::BlockManager::match_blocks)
32/// / [`BlockManager::scan_matches`](crate::manager::BlockManager::scan_matches)
33/// -- look up already-registered blocks by [`SequenceHash`].
34/// - [`WeakBlock::upgrade`] -- resurrects a weak reference if the block is
35/// still alive.
36///
37/// # State transitions
38///
39/// - [`downgrade`](Self::downgrade) -- creates a [`WeakBlock`] that does not
40/// keep the block alive.
41///
42/// # Clone behaviour
43///
44/// Cloning an `ImmutableBlock` increments `inflight_immutable`; dropping a
45/// clone decrements it. The underlying registered block is shared via
46/// `Arc`, so clones are cheap.
47///
48/// # Drop behaviour
49///
50/// Dropping the last strong reference (including internal pool references)
51/// triggers the block's return to the inactive or reset pool. Every drop
52/// decrements the `inflight_immutable` gauge.
53pub struct ImmutableBlock<T: BlockMetadata> {
54 block: Arc<dyn RegisteredBlock<T>>,
55 upgrade_fn: UpgradeFn<T>,
56 metrics: Option<Arc<BlockPoolMetrics>>,
57}
58
59/// Non-owning reference to a registered block.
60///
61/// A `WeakBlock` does not keep the underlying block alive -- if all
62/// [`ImmutableBlock`] handles (and internal pool references) are dropped,
63/// the block may be evicted and the weak reference will fail to upgrade.
64///
65/// Created via [`ImmutableBlock::downgrade`]. Cloneable and cheap to hold.
66///
67/// Call [`upgrade`](Self::upgrade) to attempt to recover a full
68/// [`ImmutableBlock`].
69#[derive(Clone)]
70pub struct WeakBlock<T: BlockMetadata> {
71 sequence_hash: SequenceHash,
72 block: Weak<dyn RegisteredBlock<T>>,
73 upgrade_fn: UpgradeFn<T>,
74 metrics: Option<Arc<BlockPoolMetrics>>,
75}
76
77impl<T: BlockMetadata> ImmutableBlock<T> {
78 /// Create a new ImmutableBlock with an upgrade function
79 pub(crate) fn new(
80 block: Arc<dyn RegisteredBlock<T>>,
81 upgrade_fn: UpgradeFn<T>,
82 metrics: Option<Arc<BlockPoolMetrics>>,
83 ) -> Self {
84 if let Some(ref m) = metrics {
85 m.inc_inflight_immutable();
86 }
87 Self {
88 block,
89 upgrade_fn,
90 metrics,
91 }
92 }
93
94 /// Creates a [`WeakBlock`] that references the same registered block
95 /// without preventing it from being evicted.
96 pub fn downgrade(&self) -> WeakBlock<T> {
97 WeakBlock {
98 sequence_hash: self.sequence_hash(),
99 block: Arc::downgrade(&self.block),
100 upgrade_fn: self.upgrade_fn.clone(),
101 metrics: self.metrics.clone(),
102 }
103 }
104
105 /// Returns the [`BlockId`] assigned to this block.
106 pub fn block_id(&self) -> BlockId {
107 self.block.block_id()
108 }
109
110 /// Returns the [`SequenceHash`] that identifies this block's content.
111 pub fn sequence_hash(&self) -> SequenceHash {
112 self.block.sequence_hash()
113 }
114
115 /// Returns a clone of the [`BlockRegistrationHandle`] for this block.
116 pub fn registration_handle(&self) -> BlockRegistrationHandle {
117 self.block.registration_handle().clone()
118 }
119
120 /// Returns the number of strong (`Arc`) references to the underlying
121 /// registered block, including internal pool references.
122 pub fn use_count(&self) -> usize {
123 Arc::strong_count(&self.block)
124 }
125}
126
127impl<T: BlockMetadata> Clone for ImmutableBlock<T> {
128 fn clone(&self) -> Self {
129 if let Some(ref m) = self.metrics {
130 m.inc_inflight_immutable();
131 }
132 Self {
133 block: self.block.clone(),
134 upgrade_fn: self.upgrade_fn.clone(),
135 metrics: self.metrics.clone(),
136 }
137 }
138}
139
140impl<T: BlockMetadata> Drop for ImmutableBlock<T> {
141 #[inline]
142 fn drop(&mut self) {
143 if let Some(ref m) = self.metrics {
144 m.dec_inflight_immutable();
145 }
146 }
147}
148
149impl<T: BlockMetadata> std::fmt::Debug for ImmutableBlock<T> {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 f.debug_struct("ImmutableBlock")
152 .field("block_id", &self.block_id())
153 .field("sequence_hash", &self.sequence_hash())
154 .finish()
155 }
156}
157
158impl<T: BlockMetadata> WeakBlock<T> {
159 /// Attempts to upgrade this weak reference back to an [`ImmutableBlock`].
160 ///
161 /// Uses a two-phase strategy:
162 /// 1. Tries a direct `Weak::upgrade` on the stored pointer (fast path).
163 /// 2. Falls back to searching the
164 /// [`BlockRegistry`](crate::registry::BlockRegistry) by
165 /// [`SequenceHash`] in case the block was moved between pools.
166 ///
167 /// Returns `None` if the block has been fully evicted.
168 pub fn upgrade(&self) -> Option<ImmutableBlock<T>> {
169 // Fast path: direct weak upgrade
170 if let Some(block) = self.block.upgrade() {
171 return Some(ImmutableBlock::new(
172 block,
173 self.upgrade_fn.clone(),
174 self.metrics.clone(),
175 ));
176 }
177
178 // Slow path: search the registry by sequence hash
179 if let Some(block) = (self.upgrade_fn)(self.sequence_hash) {
180 return Some(ImmutableBlock::new(
181 block,
182 self.upgrade_fn.clone(),
183 self.metrics.clone(),
184 ));
185 }
186
187 None
188 }
189
190 /// Returns the [`SequenceHash`] for the block this weak reference
191 /// points to.
192 pub fn sequence_hash(&self) -> SequenceHash {
193 self.sequence_hash
194 }
195}
196
197impl<T: BlockMetadata> std::fmt::Debug for WeakBlock<T> {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 f.debug_struct("WeakBlock")
200 .field("sequence_hash", &self.sequence_hash())
201 .finish()
202 }
203}