Skip to main content

ferrum_interfaces/vnext/resource/
runtime_driver.rs

1use super::{
2    Arc, BufferRequest, DeviceAllocationError, DeviceAllocationReceipt, DeviceId, DeviceRuntime,
3    FailureDomain, FailureEnvelope, ResourceCommitView, ResourceDriverFailure,
4    ResourceOwnershipTransferFailure, ResourcePoolOwnership, ResourceReservation,
5    ResourceTransactionContext, ResourceTransactionDriver, VNextError,
6};
7
8/// Production transaction adapter for a concrete [`DeviceRuntime`].
9///
10/// Core owns reservation ordering, allocation authority, buffers, and capacity
11/// claims. This adapter therefore has no parallel allocator ledger: reserve,
12/// rollback, and release are acknowledgements, while commit consumes the exact
13/// core-issued allocation permit.
14pub struct RuntimeResourceDriver<R>
15where
16    R: DeviceRuntime,
17{
18    runtime: Arc<R>,
19    retained_ownership: Vec<ResourcePoolOwnership<R>>,
20}
21
22impl<R> RuntimeResourceDriver<R>
23where
24    R: DeviceRuntime,
25{
26    pub fn new(runtime: Arc<R>) -> Result<Self, VNextError> {
27        runtime.descriptor().validate()?;
28        Ok(Self {
29            runtime,
30            retained_ownership: Vec::new(),
31        })
32    }
33
34    pub fn runtime(&self) -> &Arc<R> {
35        &self.runtime
36    }
37
38    /// Number of pools retained after an indeterminate transaction outcome.
39    /// Normal provisioning and shutdown leave this at zero.
40    pub fn retained_pool_count(&self) -> usize {
41        self.retained_ownership.len()
42    }
43
44    fn failure(
45        code: &'static str,
46        message: impl std::fmt::Display,
47        retryable: bool,
48    ) -> ResourceDriverFailure {
49        let message = message
50            .to_string()
51            .chars()
52            .filter(|character| !character.is_control() || matches!(character, '\n' | '\t'))
53            .take(1024)
54            .collect::<String>();
55        ResourceDriverFailure::new(
56            FailureEnvelope::new(FailureDomain::Resource, code, message, retryable)
57                .expect("runtime resource driver failures use bounded static metadata"),
58        )
59        .expect("runtime resource driver failures use the resource domain")
60    }
61
62    fn allocation_failure(&self, error: DeviceAllocationError<R::Error>) -> ResourceDriverFailure {
63        match error {
64            DeviceAllocationError::Contract(error) => {
65                Self::failure("allocation_contract", error, false)
66            }
67            DeviceAllocationError::Runtime(error) => match self.runtime.describe_error(&error) {
68                Ok(report) => {
69                    Self::failure("device_allocation", report.message(), report.retryable())
70                }
71                Err(classification_error) => Self::failure(
72                    "device_allocation_unclassified",
73                    format!("{error}; classification failed: {classification_error}"),
74                    false,
75                ),
76            },
77        }
78    }
79}
80
81impl<R> ResourceTransactionDriver for RuntimeResourceDriver<R>
82where
83    R: DeviceRuntime,
84{
85    type Buffer = R::Buffer;
86    type Runtime = R;
87
88    fn runtime(&self) -> &Arc<Self::Runtime> {
89        &self.runtime
90    }
91
92    fn device_id(&self) -> &DeviceId {
93        &self.runtime.descriptor().id
94    }
95
96    fn device_runtime_implementation_fingerprint(&self) -> &str {
97        &self.runtime.descriptor().runtime_implementation_fingerprint
98    }
99
100    fn device_capacity_bytes(&self) -> u64 {
101        self.runtime.descriptor().total_memory_bytes
102    }
103
104    fn reserve_resource(
105        &mut self,
106        _context: &ResourceTransactionContext<'_, Self::Runtime>,
107        _reservation: &ResourceReservation,
108    ) -> Result<(), ResourceDriverFailure> {
109        Ok(())
110    }
111
112    fn commit_resource<'commit>(
113        &mut self,
114        context: &'commit ResourceTransactionContext<'_, Self::Runtime>,
115        reservation: &ResourceReservation,
116    ) -> Result<DeviceAllocationReceipt<'commit>, ResourceDriverFailure> {
117        let request = BufferRequest::new(
118            reservation.resource_id().clone(),
119            reservation.size_bytes(),
120            reservation.alignment_bytes(),
121            reservation.usage(),
122            reservation.element_type(),
123        )
124        .map_err(|error| Self::failure("buffer_request", error, false))?;
125        context
126            .allocate(&request)
127            .map_err(|error| self.allocation_failure(error))
128    }
129
130    fn compensate_reserve_resource(
131        &mut self,
132        _context: &ResourceTransactionContext<'_, Self::Runtime>,
133        _reservation: &ResourceReservation,
134    ) -> Result<(), ResourceDriverFailure> {
135        Ok(())
136    }
137
138    fn compensate_commit_resource(
139        &mut self,
140        _context: &ResourceTransactionContext<'_, Self::Runtime>,
141        _reservation: &ResourceReservation,
142        _buffer: &Self::Buffer,
143    ) -> Result<(), ResourceDriverFailure> {
144        Ok(())
145    }
146
147    fn rollback_resource(
148        &mut self,
149        _context: &ResourceTransactionContext<'_, Self::Runtime>,
150        _reservation: &ResourceReservation,
151    ) -> Result<(), ResourceDriverFailure> {
152        Ok(())
153    }
154
155    fn release_resource(
156        &mut self,
157        _context: &ResourceTransactionContext<'_, Self::Runtime>,
158        _reservation: &ResourceReservation,
159        _buffer: &Self::Buffer,
160    ) -> Result<(), ResourceDriverFailure> {
161        Ok(())
162    }
163
164    fn reconcile_commit_outcome(
165        &mut self,
166        _context: &ResourceTransactionContext<'_, Self::Runtime>,
167        _expected: &ResourceReservation,
168        _actual: ResourceCommitView<'_, Self::Buffer>,
169    ) -> Result<(), ResourceDriverFailure> {
170        Ok(())
171    }
172
173    fn quarantine_transaction(
174        &mut self,
175        _context: &ResourceTransactionContext<'_, Self::Runtime>,
176        ownership: ResourcePoolOwnership<Self::Runtime>,
177    ) -> Result<(), ResourceOwnershipTransferFailure<Self::Runtime>> {
178        self.retained_ownership.push(ownership);
179        Ok(())
180    }
181
182    fn abandon_transaction(&mut self, ownership: ResourcePoolOwnership<Self::Runtime>) {
183        self.retained_ownership.push(ownership);
184    }
185}