mmap_io/
flush.rs

1#![allow(dead_code)]
2//! Flush policy configuration for MemoryMappedFile.
3//!
4//! Controls when writes to a RW mapping should be flushed to disk.
5
6use parking_lot::RwLock;
7use std::sync::Arc;
8use std::thread;
9use std::time::{Duration, Instant};
10
11/// Policy controlling when to flush dirty pages to disk.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum FlushPolicy {
14    /// Never flush implicitly; flush() must be called by the user.
15    #[default]
16    Never,
17    /// Alias of Never for semantic clarity when using the builder API.
18    Manual,
19    /// Flush after every write/update_region call.
20    Always,
21    /// Flush when at least N bytes have been written since the last flush.
22    EveryBytes(usize),
23    /// Flush after every W writes (calls to update_region).
24    EveryWrites(usize),
25    /// Flush automatically every N milliseconds when there are pending writes.
26    EveryMillis(u64),
27}
28
29/// Time-based flush manager that handles automatic flushing at regular intervals.
30/// This is used internally when FlushPolicy::EveryMillis is configured.
31pub struct TimeBasedFlusher {
32    interval: Duration,
33    last_flush: Arc<RwLock<Option<Instant>>>,
34    _handle: Option<thread::JoinHandle<()>>,
35}
36
37impl TimeBasedFlusher {
38    /// Create a new time-based flusher with the given interval.
39    /// Returns None if interval_ms is 0.
40    pub fn new<F>(interval_ms: u64, flush_callback: F) -> Option<Self>
41    where
42        F: Fn() -> bool + Send + 'static,
43    {
44        if interval_ms == 0 {
45            return None;
46        }
47
48        let interval = Duration::from_millis(interval_ms);
49        let last_flush = Arc::new(RwLock::new(Some(Instant::now())));
50        let last_flush_clone = Arc::clone(&last_flush);
51
52        let handle = thread::spawn(move || {
53            loop {
54                thread::sleep(interval);
55
56                // Check if we should flush
57                let should_flush = {
58                    let last = last_flush_clone.read();
59                    if let Some(last_time) = *last {
60                        last_time.elapsed() >= interval
61                    } else {
62                        false
63                    }
64                };
65
66                if should_flush {
67                    // Attempt flush via callback
68                    let flushed = flush_callback();
69                    if flushed {
70                        // Update last flush time
71                        *last_flush_clone.write() = Some(Instant::now());
72                    }
73                }
74            }
75        });
76
77        Some(Self {
78            interval,
79            last_flush,
80            _handle: Some(handle),
81        })
82    }
83
84    /// Manually trigger an immediate flush and update the last flush time.
85    pub fn manual_flush(&self) {
86        *self.last_flush.write() = Some(Instant::now());
87    }
88
89    /// Check if it's time for a flush based on the interval.
90    pub fn should_flush(&self) -> bool {
91        let last = self.last_flush.read();
92        if let Some(last_time) = *last {
93            last_time.elapsed() >= self.interval
94        } else {
95            true // First flush
96        }
97    }
98}
99
100impl Drop for TimeBasedFlusher {
101    fn drop(&mut self) {
102        // The background thread will naturally exit when the handle is dropped
103        // We don't join here to avoid blocking the drop
104    }
105}