1 use crate::Clock; 2 use std::{ 3 io, 4 sync::{ 5 atomic::{AtomicBool, Ordering}, 6 Arc, 7 }, 8 thread::{self, JoinHandle}, 9 time::Duration, 10 }; 11 12 /// Builder for creating an upkeep task. 13 #[derive(Debug)] 14 pub struct Builder { 15 interval: Duration, 16 clock: Clock, 17 } 18 19 /// Handle to a running upkeep task. 20 /// 21 /// If a handle is dropped, the background upkeep thread it belongs to is stopped, which will stop 22 /// updating the recent time. 23 #[derive(Debug)] 24 pub struct Handle { 25 done: Arc<AtomicBool>, 26 handle: Option<JoinHandle<()>>, 27 } 28 29 impl Builder { 30 /// Creates a new [`Builder`]. 31 /// 32 /// This creates a new internal clock for acquiring the current time. If you have an existing 33 /// [`Clock`] that is already calibrated, it is slightly faster to clone it and construct the 34 /// builder with [`new_with_clock`](Builder::new_with_clock) to avoid recalibrating. new(interval: Duration) -> Builder35 pub fn new(interval: Duration) -> Builder { Self::new_with_clock(interval, Clock::new()) } 36 37 /// Creates a new [`Builder`] with the specified [`Clock`] instance. new_with_clock(interval: Duration, clock: Clock) -> Builder38 pub fn new_with_clock(interval: Duration, clock: Clock) -> Builder { Builder { interval, clock } } 39 40 /// Start the upkeep thread, periodically updating the global coarse time. 41 /// 42 /// If the return value is [`Ok(handle)`], then the thread was spawned successfully and can be 43 /// stopped by dropping the returned handle. Otherwise, [`Err`] contains the error that was 44 /// returned when trying to spawn the thread. start(self) -> Result<Handle, io::Error>45 pub fn start(self) -> Result<Handle, io::Error> { 46 let interval = self.interval; 47 let clock = self.clock; 48 49 let done = Arc::new(AtomicBool::new(false)); 50 let their_done = done.clone(); 51 52 let handle = thread::Builder::new() 53 .name("quanta-upkeep".to_string()) 54 .spawn(move || { 55 while !their_done.load(Ordering::Acquire) { 56 let now = clock.now(); 57 Clock::upkeep(now); 58 59 thread::sleep(interval); 60 } 61 })?; 62 63 Ok(Handle { 64 done, 65 handle: Some(handle), 66 }) 67 } 68 } 69 70 impl Drop for Handle { drop(&mut self)71 fn drop(&mut self) { 72 self.done.store(true, Ordering::Release); 73 74 if let Some(handle) = self.handle.take() { 75 let _ = handle 76 .join() 77 .map_err(|_| io::Error::new(io::ErrorKind::Other, "failed to stop upkeep thread")); 78 } 79 } 80 } 81