use core::alloc::{Layout, LayoutError};
const FREE: u64 = 0;
const REQUEST: u64 = 1;
const FRAMEWORK_QUEUED: u64 = 2;
const SCHEMA_IDENTITY: [u8; 32] = [
0xfb, 0xed, 0x6a, 0xcd, 0x3e, 0xd0, 0xb3, 0x2a, 0x17, 0xe2, 0x9b, 0x7c, 0xb6, 0x85, 0x89, 0xb7,
0x71, 0x19, 0x07, 0xdd, 0x91, 0x3d, 0xd5, 0x7f, 0x07, 0x22, 0xf3, 0x1a, 0x76, 0x1d, 0x1b, 0xf0,
];
#[doc(hidden)]
#[repr(C, align(8))]
#[derive(Clone, Copy)]
pub struct ObservabilityBlockState {
generation: u64,
owner: u64,
request_slot: u64,
request_generation: u64,
audit_generation: u64,
}
impl core::fmt::Debug for ObservabilityBlockState {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("ObservabilityBlockState(<opaque>)")
}
}
const _: () = assert!(core::mem::size_of::<ObservabilityBlockState>() == 40);
const _: () = assert!(core::mem::align_of::<ObservabilityBlockState>() == 8);
impl ObservabilityBlockState {
fn free() -> Self {
Self {
generation: 0,
owner: FREE,
request_slot: 0,
request_generation: 0,
audit_generation: 0,
}
}
pub fn is_free(&self) -> bool {
self.owner == FREE
}
pub fn is_request_owned(&self) -> bool {
self.owner == REQUEST
}
pub fn is_framework_queued(&self) -> bool {
self.owner == FRAMEWORK_QUEUED
}
pub fn next_request_generation(&self) -> Option<u64> {
self.is_free()
.then(|| self.generation.checked_add(1))
.flatten()
}
pub fn acquire_request(
&mut self,
slot: usize,
generation: u64,
audit_generation: u64,
) -> Option<u64> {
if !self.is_free() {
return None;
}
let block_generation = self.generation.checked_add(1)?;
let request_slot = u64::try_from(slot).ok()?;
self.generation = block_generation;
self.owner = REQUEST;
self.request_slot = request_slot;
self.request_generation = generation;
self.audit_generation = audit_generation;
Some(block_generation)
}
pub fn request_matches(
&self,
block_generation: u64,
slot: usize,
generation: u64,
audit_generation: u64,
) -> bool {
self.generation == block_generation
&& self.owner == REQUEST
&& u64::try_from(slot) == Ok(self.request_slot)
&& self.request_generation == generation
&& self.audit_generation == audit_generation
}
pub fn commit_request_to_framework(
&mut self,
block_generation: u64,
slot: usize,
generation: u64,
audit_generation: u64,
) -> bool {
if !self.request_matches(block_generation, slot, generation, audit_generation) {
return false;
}
self.clear_owner(FRAMEWORK_QUEUED);
true
}
pub fn release_framework(&mut self, block_generation: u64) -> bool {
if self.generation != block_generation || !self.is_framework_queued() {
return false;
}
self.clear_owner(FREE);
true
}
pub fn rollback_request(
&mut self,
block_generation: u64,
slot: usize,
generation: u64,
audit_generation: u64,
) -> bool {
if !self.request_matches(block_generation, slot, generation, audit_generation) {
return false;
}
self.clear_owner(FREE);
true
}
fn clear_owner(&mut self, owner: u64) {
self.owner = owner;
self.request_slot = 0;
self.request_generation = 0;
self.audit_generation = 0;
}
}
#[doc(hidden)]
pub fn observability_block_state_array_layout(blocks: usize) -> Result<Layout, LayoutError> {
Layout::array::<ObservabilityBlockState>(blocks)
}
#[doc(hidden)]
pub fn allocate_observability_block_states(
blocks: usize,
) -> Result<Box<[ObservabilityBlockState]>, std::collections::TryReserveError> {
let mut states = Vec::new();
states.try_reserve_exact(blocks)?;
states.resize(blocks, ObservabilityBlockState::free());
Ok(states.into_boxed_slice())
}
#[doc(hidden)]
pub const fn observability_block_state_schema_identity() -> [u8; 32] {
SCHEMA_IDENTITY
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_and_transitions_are_stable() {
assert_eq!(core::mem::size_of::<ObservabilityBlockState>(), 40);
assert_eq!(core::mem::align_of::<ObservabilityBlockState>(), 8);
assert_eq!(
observability_block_state_array_layout(4).unwrap().size(),
160
);
assert_ne!(observability_block_state_schema_identity(), [0; 32]);
let mut states = allocate_observability_block_states(1).unwrap();
let state = &mut states[0];
assert_eq!(state.acquire_request(3, 7, 11), Some(1));
assert!(state.request_matches(1, 3, 7, 11));
assert!(state.commit_request_to_framework(1, 3, 7, 11));
assert!(state.release_framework(1));
assert!(state.is_free());
assert_eq!(state.acquire_request(4, 8, 12), Some(2));
assert!(state.rollback_request(2, 4, 8, 12));
}
}