use commonware_codec::{Error as CodecError, FixedSize, Read, Write};
use commonware_parallel::{Rayon, Strategy as _};
use commonware_runtime::{Buf, BufMut};
use commonware_utils::sync::Mutex;
use std::{
sync::{Arc, mpsc},
thread,
time::Duration,
};
pub(crate) fn block_strategy(strategy: &Rayon, workers: usize) -> mpsc::Sender<()> {
let manual = strategy.manual();
assert!(
manual.parallelism() >= 2,
"block_strategy requires a multi-worker pool"
);
let (started_tx, started_rx) = mpsc::channel();
let (release_tx, release_rx) = mpsc::channel();
let release_rx = Arc::new(Mutex::new(release_rx));
for _ in 0..workers {
let started_tx = started_tx.clone();
let release_rx = Arc::clone(&release_rx);
drop(manual.spawn(1, move |_| {
started_tx.send(()).unwrap();
let _ = release_rx.lock().recv();
}));
}
drop(started_tx);
for _ in 0..workers {
started_rx
.recv_timeout(Duration::from_secs(10))
.expect("strategy worker did not start");
}
release_tx
}
pub(crate) struct DropMonitor<T> {
inner: T,
clean_drop: Option<mpsc::Sender<bool>>,
}
impl<T> DropMonitor<T> {
pub(crate) const fn untracked(inner: T) -> Self {
Self {
inner,
clean_drop: None,
}
}
pub(crate) fn tracked(inner: T) -> (Self, mpsc::Receiver<bool>) {
let (clean_drop, receiver) = mpsc::channel();
(
Self {
inner,
clean_drop: Some(clean_drop),
},
receiver,
)
}
}
impl<T: FixedSize> FixedSize for DropMonitor<T> {
const SIZE: usize = T::SIZE;
}
impl<T: Write> Write for DropMonitor<T> {
fn write(&self, buf: &mut impl BufMut) {
self.inner.write(buf);
}
}
impl<T: Read> Read for DropMonitor<T> {
type Cfg = T::Cfg;
fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
T::read_cfg(buf, cfg).map(Self::untracked)
}
}
impl<T> Drop for DropMonitor<T> {
fn drop(&mut self) {
if let Some(clean_drop) = self.clean_drop.take() {
let _ = clean_drop.send(!thread::panicking());
}
}
}