chamber_backup/service.rs
1use crate::manager::{BackupManager, VaultOperations};
2use chamber_vault::BackupConfig;
3use std::sync::{Arc, Mutex};
4use std::thread;
5use std::time::Duration;
6pub struct BackgroundService<V: VaultOperations + Send + 'static> {
7 backup_manager: Arc<Mutex<BackupManager<V>>>,
8 running: Arc<Mutex<bool>>,
9}
10
11impl<V: VaultOperations + Send + 'static> BackgroundService<V> {
12 pub fn new(vault: V, config: BackupConfig) -> Self {
13 let backup_manager = Arc::new(Mutex::new(BackupManager::new(vault, config)));
14 let running = Arc::new(Mutex::new(false));
15
16 Self {
17 backup_manager,
18 running,
19 }
20 }
21
22 /// Starts the backup monitoring thread if it is not already running.
23 ///
24 /// This function initiates a thread that periodically checks whether a backup
25 /// operation is needed. It acquires a lock on `self.running` to ensure that
26 /// only one monitoring thread can run at a time. Once started, the thread will
27 /// perform the following steps in a loop:
28 /// 1. Check the current state of the `running` flag to determine if the thread
29 /// should continue execution.
30 /// 2. Attempt to acquire a lock on the `backup_manager` to access the backup logic.
31 /// 3. If the backup manager determines a backup is needed, attempt to execute the
32 /// backup. If the backup operation fails, an error message will be printed to
33 /// standard error. (Optional: Notification logic can be implemented here.)
34 /// 4. Sleep for one hour before the next backup check.
35 ///
36 /// ## Remarks
37 /// - The function uses `thread::spawn` to create a separate thread for backup
38 /// checks, enabling it to run independently of the main thread.
39 /// - The sleep interval is hardcoded to 1 hour (`3600` seconds).
40 /// - Errors during locking (`self.running` or `backup_manager`) are handled with
41 /// `.expect` to provide immediate feedback when locks fail to acquire.
42 /// - Clippy warnings for the use of `.expect` are explicitly disabled, as they
43 /// are considered acceptable in this context to handle critical locking issues.
44 ///
45 /// ## Panics
46 /// The function may panic in the following cases:
47 /// - If acquiring the lock on `self.running` or `self.backup_manager` fails.
48 /// - If any lock operation encounters a thread-poisoning error.
49 ///
50 /// ## Thread Safety
51 /// - The function ensures proper synchronization by using a `Mutex` for the
52 /// `running` flag and `backup_manager`.
53 /// - The `running` and `backup_manager` are wrapped in `Arc` (Atomically Reference Counted),
54 /// allowing shared ownership across threads.
55 ///
56 /// ## Lifetime
57 /// - The thread will run continuously, checking the backup condition every hour, until
58 /// the `running` flag is set to `false`.
59 ///
60 /// ## Dependencies
61 /// - `std::thread` for spawning the thread and sleeping.
62 /// - `std::sync::{Arc, Mutex}` for synchronization.
63 /// - `std::time::Duration` for specifying the sleep duration.
64 ///
65 /// ## Notes
66 /// - Before calling `start`, ensure that the `BackupService` instance has been correctly
67 /// initialized with a `backup_manager` capable of handling the `backup_if_needed` logic.
68 /// ```
69 pub fn start(&self) {
70 {
71 #[allow(clippy::expect_used)]
72 let mut running = self.running.lock().expect("Unable to acquire the lock for running");
73 if *running {
74 return; // Already running
75 }
76 *running = true;
77 }
78
79 let backup_manager = Arc::clone(&self.backup_manager);
80 let running = Arc::clone(&self.running);
81
82 #[allow(clippy::expect_used)]
83 thread::spawn(move || {
84 while *running.lock().expect("Unable to acquire the lock for running") {
85 // Check for backup every hour
86 if let Ok(mut manager) = backup_manager.lock() {
87 if let Err(e) = manager.backup_if_needed() {
88 eprintln!("Backup failed: {e}");
89 // Could send notification here
90 }
91 }
92
93 // Sleep for 1 hour
94 thread::sleep(Duration::from_secs(3600));
95 }
96 });
97 }
98
99 /// Stops the current process or thread associated with the instance by setting the `running` flag
100 /// to `false`. This effectively signals that the process or thread should terminate its execution.
101 ///
102 /// # Behavior
103 /// - Acquires a lock on the `self.running` mutex to safely modify the shared `running` state.
104 /// - If the lock cannot be acquired, it will panic with the message "Unable to get the lock (running)".
105 /// - Updates the `running` boolean to `false` to indicate that the process or thread should stop.
106 ///
107 /// # Panics
108 /// - This method will panic if it fails to acquire the lock for `self.running`. This panic may
109 /// occur due to a poisoned lock or other concurrency issues.
110 ///
111 /// # Allowances
112 /// - The `clippy::expect_used` lint is explicitly allowed to permit the use of `.expect()` for
113 /// descriptive error handling in the context of this critical locking operation.
114 #[allow(clippy::expect_used)]
115 pub fn stop(&self) {
116 let mut running = self.running.lock().expect("Unable to acquire the lock for running");
117 *running = false;
118 }
119}