use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use crate::allocator::{AllocationTotals, allocation_totals};
use crate::{ERR_POISONED_LOCK, Operation, OperationMetrics};
#[derive(Debug)]
#[must_use = "Measurements are taken between creation and drop"]
pub struct ProcessSpan {
metrics: Arc<Mutex<OperationMetrics>>,
start_bytes: u64,
start_count: u64,
iterations: u64,
_not_sync: PhantomData<*mut ()>,
}
unsafe impl Send for ProcessSpan {}
impl ProcessSpan {
pub(crate) fn new(operation: &Operation, iterations: u64) -> Self {
assert!(iterations != 0);
let AllocationTotals {
bytes: start_bytes,
count: start_count,
} = allocation_totals();
Self {
metrics: operation.metrics(),
start_bytes,
start_count,
iterations,
_not_sync: PhantomData,
}
}
pub fn iterations(mut self, iterations: u64) -> Self {
assert!(iterations != 0, "Iterations cannot be zero");
self.iterations = iterations;
self
}
#[must_use]
#[cfg_attr(test, mutants::skip)] fn to_deltas(&self) -> (u64, u64) {
let AllocationTotals {
bytes: current_bytes,
count: current_count,
} = allocation_totals();
let total_bytes_delta = current_bytes
.checked_sub(self.start_bytes)
.expect("total bytes allocated could not possibly decrease");
let total_count_delta = current_count
.checked_sub(self.start_count)
.expect("total allocations count could not possibly decrease");
if self.iterations > 1 {
let bytes_delta = total_bytes_delta
.checked_div(self.iterations)
.expect("guarded by if condition");
let count_delta = total_count_delta
.checked_div(self.iterations)
.expect("guarded by if condition");
(bytes_delta, count_delta)
} else {
(total_bytes_delta, total_count_delta)
}
}
}
impl Drop for ProcessSpan {
fn drop(&mut self) {
let (bytes_delta, count_delta) = self.to_deltas();
let mut data = self.metrics.lock().expect(ERR_POISONED_LOCK);
data.add_iterations(bytes_delta, count_delta, self.iterations);
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::*;
static_assertions::assert_impl_all!(ProcessSpan: Send, UnwindSafe, RefUnwindSafe);
static_assertions::assert_not_impl_any!(ProcessSpan: Sync);
}