kvbm_logical/blocks/complete.rs
1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! RAII guard for a block in the **Staged** state.
5//!
6//! A [`CompleteBlock`] holds a slot that has been assigned a
7//! [`SequenceHash`] but has not yet been registered. It is produced by
8//! [`MutableBlock::stage`](super::MutableBlock::stage) or
9//! [`MutableBlock::complete`](super::MutableBlock::complete) and can be
10//! either registered via
11//! [`BlockManager::register_block`](crate::manager::BlockManager::register_block)
12//! or rolled back to a [`MutableBlock`] with [`reset`](CompleteBlock::reset).
13
14use std::sync::Arc;
15
16use crate::blocks::{BlockId, BlockMetadata, MutableBlock, SequenceHash};
17use crate::pools::BlockStore;
18
19/// RAII guard for a block in the **Staged** state.
20pub struct CompleteBlock<T: BlockMetadata> {
21 store: Arc<BlockStore<T>>,
22 block_id: BlockId,
23 block_size: usize,
24 seq_hash: SequenceHash,
25 /// `false` once the guard has been consumed by `reset` or by
26 /// `BlockManager::register_block`; Drop becomes a no-op.
27 armed: bool,
28}
29
30impl<T: BlockMetadata + Sync> CompleteBlock<T> {
31 pub(crate) fn from_store(
32 store: Arc<BlockStore<T>>,
33 block_id: BlockId,
34 block_size: usize,
35 seq_hash: SequenceHash,
36 ) -> Self {
37 Self {
38 store,
39 block_id,
40 block_size,
41 seq_hash,
42 armed: true,
43 }
44 }
45
46 /// Returns the [`BlockId`] assigned to this block.
47 pub fn block_id(&self) -> BlockId {
48 self.block_id
49 }
50
51 /// Returns the fixed block size of this block in tokens.
52 pub fn block_size(&self) -> usize {
53 self.block_size
54 }
55
56 /// Returns the [`SequenceHash`] assigned during staging.
57 pub fn sequence_hash(&self) -> SequenceHash {
58 self.seq_hash
59 }
60
61 /// Roll back to a [`MutableBlock`].
62 pub fn reset(mut self) -> MutableBlock<T> {
63 self.store.transition_back_to_mutable(self.block_id);
64 self.armed = false;
65 MutableBlock::from_store(self.store.clone(), self.block_id, self.block_size)
66 }
67
68 /// Disarm the guard so Drop is a no-op. Used by registration when
69 /// the slot will be transitioned `Staged → Primary` / `Duplicate`
70 /// under the store mutex; the caller takes responsibility for the
71 /// slot's onward state.
72 pub(crate) fn disarm(&mut self) {
73 self.armed = false;
74 }
75
76 /// Re-arm a previously disarmed guard so Drop releases the slot
77 /// `Staged → Reset`. Used by the registration `Reject` path after
78 /// the lookup commits to dropping the staged block.
79 pub(crate) fn rearm(&mut self) {
80 self.armed = true;
81 }
82}
83
84impl<T: BlockMetadata> Drop for CompleteBlock<T> {
85 #[inline]
86 fn drop(&mut self) {
87 if self.armed {
88 self.store.release_staged(self.block_id);
89 }
90 }
91}
92
93impl<T: BlockMetadata> std::fmt::Debug for CompleteBlock<T> {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("CompleteBlock")
96 .field("block_id", &self.block_id)
97 .field("seq_hash", &self.seq_hash)
98 .finish()
99 }
100}