use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use crate::allocator::get_or_init_thread_counters;
use crate::{ERR_POISONED_LOCK, Operation, OperationMetrics};
#[derive(Debug)]
#[must_use = "Measurements are taken between creation and drop"]
pub struct ThreadSpan {
metrics: Arc<Mutex<OperationMetrics>>,
start_bytes: u64,
start_count: u64,
iterations: u64,
_single_threaded: PhantomData<*const ()>,
}
impl ThreadSpan {
pub(crate) fn new(operation: &Operation, iterations: u64) -> Self {
assert!(iterations != 0);
let counters = get_or_init_thread_counters();
let start_bytes = counters.bytes();
let start_count = counters.count();
Self {
metrics: operation.metrics(),
start_bytes,
start_count,
iterations,
_single_threaded: 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 counters = get_or_init_thread_counters();
let current_bytes = counters.bytes();
let current_count = counters.count();
let total_bytes_delta = current_bytes
.checked_sub(self.start_bytes)
.expect("thread bytes allocated could not possibly decrease");
let total_count_delta = current_count
.checked_sub(self.start_count)
.expect("thread 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 ThreadSpan {
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 super::*;
use std::panic::RefUnwindSafe;
use std::panic::UnwindSafe;
static_assertions::assert_not_impl_all!(ThreadSpan: Send);
static_assertions::assert_not_impl_all!(ThreadSpan: Sync);
static_assertions::assert_impl_all!(
ThreadSpan: UnwindSafe, RefUnwindSafe
);
}