blob: bc80a21d2b50f26f1d294022ea939f97e17f39e6 [file]
// Copyright 2026 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// Tracks the number of populated slots in the VmCowPages' local page list. If the VmCowPages
/// changes a slot to being populated, or vice versa, that should be reported to
/// `ContinuousAttributionTracker`.
///
/// This struct is not thread safe and should only be accessed under the lock of the associated
/// `VmCowPages`.
#[repr(C)]
#[derive(Debug, Default, PartialEq, Eq)]
pub struct ContinuousAttributionTracker {
/// The number of populated slots in the local page list.
current_slots: u32,
/// The greatest number of `current_slots` since the high-water mark value was last reset.
///
/// Always greater than or equal to `current_slots`.
hwm_slots: u32,
}
// ContinuousAttributionTracker uses a 32-bit count to represent the number of populated slots in
// the local page list. Since a ContinuousAttributionTracker is intended to be stored inline in a
// VmCowPages, reducing its size (by not using a 64-bit count) is a substantial memory saving for
// the system.
zr::static_assert!(core::mem::size_of::<ContinuousAttributionTracker>() == 8);
zr::static_assert!(core::mem::align_of::<ContinuousAttributionTracker>() == 4);
impl ContinuousAttributionTracker {
/// Constructs a new `ContinuousAttributionTracker` initialized to zero.
pub const fn new() -> Self {
Self { current_slots: 0, hwm_slots: 0 }
}
/// Move and move assignment zero the `source`.
pub fn take_from(&mut self, source: &mut Self) {
*self = core::mem::take(source);
}
/// Returns the tracked count of populated slots.
pub fn fetch_current(&self) -> u32 {
self.current_slots
}
/// Get the greatest number of populated slots since the statistic was last reset.
///
/// Resets the high-water mark.
pub fn fetch_hwm_and_reset(&mut self) -> u32 {
debug_assert!(self.hwm_slots >= self.current_slots);
let ret = self.hwm_slots;
self.hwm_slots = self.current_slots; // reset
ret
}
/// Increments the count by `by`. This quantity must be strictly positive.
pub fn increment(&mut self, by: u32) {
debug_assert!(by > 0);
let (new_slots, did_overflow) = self.current_slots.overflowing_add(by);
debug_assert!(!did_overflow);
self.current_slots = new_slots;
self.hwm_slots = core::cmp::max(self.hwm_slots, self.current_slots);
}
/// Decrements the count by `by`. This quantity must be strictly positive.
pub fn decrement(&mut self, by: u32) {
debug_assert!(by > 0);
let (new_slots, did_overflow) = self.current_slots.overflowing_sub(by);
// This overflows when there is untracked addition of content: addition of pages,
// references, or parent content markers to the page list of a VmCowPages that is not
// paired with updates to this continuous attribution tracker.
debug_assert!(!did_overflow);
self.current_slots = new_slots;
debug_assert!(self.hwm_slots >= self.current_slots);
}
}
/// The stub continuous attribution tracker. This object stores no data. Intended to be used in
/// place of the regular continuous attribution tracker, unless users opt-in to its existence.
#[repr(C)]
#[derive(Default, Debug, PartialEq, Eq)]
pub struct StubContinuousAttributionTracker;
// The continuous attribution tracker supports an empty "stubbed out" state.
zr::static_assert!(core::mem::size_of::<StubContinuousAttributionTracker>() == 0);
zr::static_assert!(core::mem::align_of::<StubContinuousAttributionTracker>() == 1);
impl StubContinuousAttributionTracker {
/// Constructs a new `StubContinuousAttributionTracker`.
pub const fn new() -> Self {
Self
}
/// Unconditionally panics.
pub fn fetch_current(&self) -> u32 {
panic!("stub");
}
/// Unconditionally panics.
pub fn fetch_hwm_and_reset(&mut self) -> u32 {
panic!("stub");
}
/// Increments the count by `by`.
pub fn increment(&mut self, _by: u32) {}
/// Decrements the count by `by`.
pub fn decrement(&mut self, _by: u32) {}
}
// C FFI exports
/// # Safety
///
/// `tracker` must be a valid, writable pointer to `ContinuousAttributionTracker` storage.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn rust_continuous_attribution_tracker_move_init(
tracker: *mut ContinuousAttributionTracker,
source: &mut ContinuousAttributionTracker,
) {
// SAFETY: `tracker` is valid for writes.
unsafe {
tracker.write(core::mem::take(source));
}
}
/// Assigns data from `source` to `tracker`, zeroing `source`.
#[unsafe(no_mangle)]
pub extern "C" fn rust_continuous_attribution_tracker_move_assign(
tracker: &mut ContinuousAttributionTracker,
source: &mut ContinuousAttributionTracker,
) {
tracker.take_from(source);
}
/// Returns the tracked count of populated slots.
#[unsafe(no_mangle)]
pub extern "C" fn rust_continuous_attribution_tracker_fetch_current(
tracker: &ContinuousAttributionTracker,
) -> u32 {
tracker.fetch_current()
}
/// Resets the high-water mark and returns the previous value.
#[unsafe(no_mangle)]
pub extern "C" fn rust_continuous_attribution_tracker_fetch_hwm_and_reset(
tracker: &mut ContinuousAttributionTracker,
) -> u32 {
tracker.fetch_hwm_and_reset()
}
/// Increments the populated slot count by `by`.
#[unsafe(no_mangle)]
pub extern "C" fn rust_continuous_attribution_tracker_increment(
tracker: &mut ContinuousAttributionTracker,
by: u32,
) {
tracker.increment(by);
}
/// Decrements the populated slot count by `by`.
#[unsafe(no_mangle)]
pub extern "C" fn rust_continuous_attribution_tracker_decrement(
tracker: &mut ContinuousAttributionTracker,
by: u32,
) {
tracker.decrement(by);
}