use super::super::*;
use crate::durable::SaveWait;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{mpsc, Arc, Mutex};
use std::time::Duration;
#[derive(Default)]
pub(super) struct MemoryPersistence {
saved: Mutex<Option<(ObjectIdentifier, NotificationClassSnapshot)>>,
pub(super) fail: AtomicBool,
saves: AtomicU64,
hold: Mutex<Option<Hold>>,
}
struct Hold {
started: mpsc::Sender<NotificationClassSnapshot>,
go: mpsc::Receiver<()>,
}
pub(super) struct Held {
pub(super) started: mpsc::Receiver<NotificationClassSnapshot>,
pub(super) go: mpsc::Sender<()>,
}
pub(super) const WAIT: Duration = Duration::from_secs(10);
impl MemoryPersistence {
pub(super) fn hold(&self) -> Held {
let (started, started_rx) = mpsc::channel();
let (go_tx, go) = mpsc::channel();
*self.hold.lock().unwrap() = Some(Hold { started, go });
Held {
started: started_rx,
go: go_tx,
}
}
pub(super) fn snapshot(&self) -> Option<NotificationClassSnapshot> {
self.saved
.lock()
.unwrap()
.clone()
.map(|(_, snapshot)| snapshot)
}
pub(super) fn saved(&self) -> Option<Vec<BACnetDestination>> {
self.snapshot().and_then(|snapshot| snapshot.recipient_list)
}
pub(super) fn saves(&self) -> u64 {
self.saves.load(Ordering::SeqCst)
}
pub(super) fn preload(&self, class: ObjectIdentifier, snapshot: NotificationClassSnapshot) {
*self.saved.lock().unwrap() = Some((class, snapshot));
}
}
impl NotificationClassPersistence for MemoryPersistence {
fn load(&self, class: ObjectIdentifier) -> Result<Option<NotificationClassSnapshot>, Error> {
Ok(self
.saved
.lock()
.unwrap()
.clone()
.filter(|(oid, _)| *oid == class)
.map(|(_, snapshot)| snapshot))
}
fn save(
&self,
class: ObjectIdentifier,
snapshot: &NotificationClassSnapshot,
) -> Result<(), Error> {
if let Some(hold) = &*self.hold.lock().unwrap() {
let _ = hold.started.send(snapshot.clone());
let _ = hold.go.recv();
}
if self.fail.load(Ordering::SeqCst) {
return Err(Error::Encoding("storage unavailable".into()));
}
*self.saved.lock().unwrap() = Some((class, snapshot.clone()));
self.saves.fetch_add(1, Ordering::SeqCst);
Ok(())
}
}
pub(super) fn persistent(storage: &Arc<MemoryPersistence>) -> NotificationClass {
NotificationClass::with_persistence(
1,
"NC",
Arc::clone(storage) as Arc<dyn NotificationClassPersistence>,
)
.unwrap()
}
pub(super) fn block_on(wait: &SaveWait) {
let (done, finished) = mpsc::channel();
let blocking = wait.clone();
std::thread::spawn(move || {
blocking.block();
let _ = done.send(());
});
finished
.recv_timeout(WAIT)
.expect("the wait did not end in time");
}