use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use crate::weight_matrix::WeightMatrix;
pub type Observer = dyn Fn(&WeightMatrix, &[f32], usize) + Send + Sync;
static INSTALLED: AtomicBool = AtomicBool::new(false);
static OBSERVER: RwLock<Option<Arc<Observer>>> = RwLock::new(None);
pub fn install(observer: Arc<Observer>) -> Result<TapGuard, AlreadyInstalled> {
let mut slot = OBSERVER.write().unwrap_or_else(|e| e.into_inner());
if slot.is_some() {
return Err(AlreadyInstalled);
}
*slot = Some(observer);
INSTALLED.store(true, Ordering::Release);
Ok(TapGuard(()))
}
#[must_use = "dropping the guard uninstalls the tap immediately"]
pub struct TapGuard(());
impl Drop for TapGuard {
fn drop(&mut self) {
INSTALLED.store(false, Ordering::Release);
*OBSERVER.write().unwrap_or_else(|e| e.into_inner()) = None;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AlreadyInstalled;
impl std::fmt::Display for AlreadyInstalled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an activation tap is already installed in this process")
}
}
impl std::error::Error for AlreadyInstalled {}
#[inline]
pub(crate) fn observe(matrix: &WeightMatrix, rows: &[f32], n_rows: usize) {
if !INSTALLED.load(Ordering::Acquire) {
return;
}
let observer = OBSERVER
.read()
.unwrap_or_else(|e| e.into_inner())
.as_ref()
.cloned();
if let Some(observer) = observer {
observer(matrix, rows, n_rows);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tensor::Tensor;
use std::sync::Mutex;
static SERIAL: Mutex<()> = Mutex::new(());
fn small_matrix() -> WeightMatrix {
WeightMatrix::F32(Tensor::new(vec![1.0; 8], vec![2, 4]))
}
#[test]
fn the_observer_sees_every_batch_row_once_and_the_matrix_identity() {
let _serial = SERIAL.lock().unwrap_or_else(|e| e.into_inner());
let m = small_matrix();
let addr = &m as *const WeightMatrix as usize;
type Seen = Mutex<Vec<(usize, Vec<f32>, usize)>>;
let seen: Arc<Seen> = Arc::new(Mutex::new(Vec::new()));
let sink = seen.clone();
let guard = install(Arc::new(move |w: &WeightMatrix, rows: &[f32], n: usize| {
sink.lock()
.unwrap()
.push((w as *const WeightMatrix as usize, rows.to_vec(), n));
}))
.unwrap();
let x: Vec<f32> = (0..12).map(|i| i as f32).collect();
let _ = m.apply_batch(&x, 3);
drop(guard);
let seen = seen.lock().unwrap();
assert_eq!(seen.len(), 1, "one batch call, one observation");
assert_eq!(seen[0].0, addr);
assert_eq!(seen[0].1, x);
assert_eq!(seen[0].2, 3);
}
#[test]
fn the_tap_is_gone_after_the_guard_drops_and_cannot_be_installed_twice() {
let _serial = SERIAL.lock().unwrap_or_else(|e| e.into_inner());
let m = small_matrix();
let calls = Arc::new(Mutex::new(0usize));
let c = calls.clone();
let guard = install(Arc::new(move |_: &WeightMatrix, _: &[f32], _| {
*c.lock().unwrap() += 1;
}))
.unwrap();
assert_eq!(
install(Arc::new(|_: &WeightMatrix, _: &[f32], _| {}))
.err()
.map(|_| ()),
Some(()),
"a second install must be refused while the first is live"
);
let _ = m.apply(&[1.0; 4]);
drop(guard);
let _ = m.apply(&[1.0; 4]);
assert_eq!(*calls.lock().unwrap(), 1);
}
}