use crate::aggregator::AggRegistry;
use crate::file_sink::FileSink;
use crate::output::write_header;
use crate::shutdown;
use crate::time::CalibrationData;
use std::cell::Cell;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::{Arc, Mutex};
pub struct ProfileSession {
pub(crate) calibration: CalibrationData,
pub(crate) cpu_time_enabled: bool,
pub(crate) agg_registry: Arc<AggRegistry>,
}
unsafe impl Send for ProfileSession {}
unsafe impl Sync for ProfileSession {}
static GLOBAL_SESSION: AtomicPtr<ProfileSession> = AtomicPtr::new(std::ptr::null_mut());
thread_local! {
static THREAD_SESSION: Cell<*const ProfileSession> = const { Cell::new(std::ptr::null()) };
}
impl ProfileSession {
pub fn init(
file_sink: Option<Arc<FileSink>>,
cpu_time_enabled: bool,
names: &'static [(u32, &'static str)],
run_id: &str,
timestamp_ms: u128,
) -> &'static Self {
let calibration = CalibrationData::calibrate();
let agg_registry: Arc<AggRegistry> = Arc::new(Mutex::new(Vec::new()));
if let Some(ref fs) = file_sink {
let mut file = fs.lock();
if write_header(
&mut *file,
names,
calibration.bias_ns(),
calibration.cpu_bias_ns(),
run_id,
timestamp_ms,
)
.is_err()
{
fs.record_io_error();
}
if std::io::Write::flush(&mut *file).is_err() {
fs.record_io_error();
}
drop(file);
shutdown::register(Arc::clone(fs), names, Arc::clone(&agg_registry));
}
let session = Box::new(Self {
calibration,
cpu_time_enabled,
agg_registry,
});
let ptr = Box::into_raw(session);
GLOBAL_SESSION.store(ptr, Ordering::Release);
let _ = THREAD_SESSION.try_with(|c| c.set(ptr));
unsafe { &*ptr }
}
#[inline(always)]
pub fn get() -> Option<&'static Self> {
let ptr = THREAD_SESSION
.try_with(|c| {
let p = c.get();
if !p.is_null() {
return p;
}
let global = GLOBAL_SESSION.load(Ordering::Acquire);
if !global.is_null() {
c.set(global);
}
global
})
.unwrap_or_else(|_| GLOBAL_SESSION.load(Ordering::Acquire));
if ptr.is_null() {
None
} else {
Some(unsafe { &*ptr })
}
}
}