use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
pub const DEFAULT_ERASER_INTERVAL_MS: u64 = 1_000;
#[derive(Debug, Clone)]
pub struct EraseRequest {
pub file_number: u32,
pub file_offset: u64,
pub byte_count: usize,
}
pub struct DataEraser {
queue: Arc<Mutex<Vec<EraseRequest>>>,
shutdown: Arc<AtomicBool>,
handle: Option<thread::JoinHandle<()>>,
active: bool,
}
impl DataEraser {
pub fn new() -> Self {
DataEraser {
queue: Arc::new(Mutex::new(Vec::new())),
shutdown: Arc::new(AtomicBool::new(false)),
handle: None,
active: false,
}
}
pub fn start(&mut self) {
let queue = Arc::clone(&self.queue);
let shutdown = Arc::clone(&self.shutdown);
let handle = thread::Builder::new()
.name("noxu-data-eraser".to_string())
.spawn(move || {
while !shutdown.load(Ordering::Relaxed) {
let requests: Vec<EraseRequest> = {
let mut q = queue.lock().unwrap();
std::mem::take(&mut *q)
};
for _req in requests {
}
thread::sleep(Duration::from_millis(
DEFAULT_ERASER_INTERVAL_MS,
));
}
})
.expect("failed to spawn noxu-data-eraser thread");
self.handle = Some(handle);
self.active = true;
}
pub fn enqueue_erase(&self, request: EraseRequest) {
if self.active {
self.queue.lock().unwrap().push(request);
}
}
pub fn pending_count(&self) -> usize {
self.queue.lock().unwrap().len()
}
pub fn is_active(&self) -> bool {
self.active
}
pub fn shutdown(&mut self) {
self.shutdown.store(true, Ordering::Relaxed);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
self.active = false;
}
}
impl Default for DataEraser {
fn default() -> Self {
Self::new()
}
}
impl Drop for DataEraser {
fn drop(&mut self) {
if self.active {
self.shutdown();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_inactive() {
let eraser = DataEraser::new();
assert!(!eraser.is_active());
assert_eq!(eraser.pending_count(), 0);
}
#[test]
fn test_enqueue_before_start_is_noop() {
let eraser = DataEraser::new();
eraser.enqueue_erase(EraseRequest {
file_number: 1,
file_offset: 100,
byte_count: 50,
});
assert_eq!(eraser.pending_count(), 0);
}
#[test]
fn test_start_and_shutdown() {
let mut eraser = DataEraser::new();
eraser.start();
assert!(eraser.is_active());
eraser.shutdown();
assert!(!eraser.is_active());
}
}