Skip to main content

hawdb_core/cancellation/
memory.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use super::*;
16
17/// Ownership of memory admitted before an allocation is attempted.
18/// Keep this lease until the allocation is destroyed, including retained state.
19pub trait RuntimeMemoryPermit: std::fmt::Debug + Send + Sync {
20    fn bytes(&self) -> u64;
21
22    /// Combine compatible allocation ownership without admitting new backing.
23    /// On failure both charges stay unchanged and the incoming lease returns.
24    /// On success retain this lease until both backing allocations are destroyed.
25    fn merge(
26        &mut self,
27        incoming: Box<dyn RuntimeMemoryPermit>,
28    ) -> Result<(), Box<dyn RuntimeMemoryPermit>> {
29        Err(incoming)
30    }
31
32    /// Optional backend identity for merging compatible concrete leases.
33    #[doc(hidden)]
34    fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
35        None
36    }
37}
38
39/// A shared allocation ledger belonging to an already admitted task.
40/// Controller ownership does not keep execution slots alive after task closure.
41pub trait RuntimeMemoryController: std::fmt::Debug + Send + Sync {
42    fn reserve(
43        &self,
44        bytes: u64,
45        ceiling: u64,
46    ) -> Result<Box<dyn RuntimeMemoryPermit>, RuntimeMemoryError>;
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum RuntimeMemoryError {
51    Stopped(RuntimeCancellationReason),
52    ReservationExceeded {
53        requested_bytes: u64,
54        available_bytes: u64,
55    },
56    /// A concrete allocation was denied by its resource ledger. A retryable
57    /// denial may recover after pressure or retained allocations change.
58    AdmissionDenied {
59        requested_bytes: u64,
60        available_bytes: u64,
61        retryable: bool,
62    },
63    Closed,
64    Pressure,
65}
66
67impl Display for RuntimeMemoryError {
68    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
69        match self {
70            Self::Stopped(reason) => Display::fmt(reason, formatter),
71            Self::ReservationExceeded { requested_bytes, available_bytes } => write!(formatter,
72                "runtime memory allocation requested {requested_bytes} bytes, exceeding the {available_bytes}-byte remaining reservation"),
73            Self::AdmissionDenied { requested_bytes, available_bytes, retryable } => write!(formatter,
74                "runtime memory allocation admission denied: requested {requested_bytes} bytes, available {available_bytes} bytes, retryable {retryable}"),
75            Self::Closed => formatter.write_str("runtime memory reservation is closed"),
76            Self::Pressure => formatter.write_str("background memory pressure defers allocation"),
77        }
78    }
79}
80
81impl Error for RuntimeMemoryError {
82    fn source(&self) -> Option<&(dyn Error + 'static)> {
83        match self {
84            Self::Stopped(reason) => Some(reason),
85            _ => None,
86        }
87    }
88}