Execute `f` while holding an exclusive file lock on the session's accumulator. The lock file is `{session_dir}/graph.lock`. The callback receives a mutable `ProvenanceAccumulator`. If `f` returns `true`, the accumulator is saved back to disk before the lock is released. If `f` returns `false`, the accumulator is discarded (read-only access). Best-effort: returns `None` if the lock or load fai
(&self, session_id: &str, f: F)
| 51 | /// |
| 52 | /// Best-effort: returns `None` if the lock or load fails. |
| 53 | fn with_accumulator<F>(&self, session_id: &str, f: F) -> Option<ProvenanceAccumulator> |
| 54 | where |
| 55 | F: FnOnce(&mut ProvenanceAccumulator) -> bool, |
| 56 | { |
| 57 | use fs2::FileExt; |
| 58 | |
| 59 | let dir = self.session_graph_dir(session_id); |
| 60 | |
| 61 | // Ensure the directory exists before creating the lock file. |
| 62 | if let Err(e) = std::fs::create_dir_all(&dir) { |
| 63 | log::warn!("Failed to create session dir for {}: {}", session_id, e,); |
| 64 | return None; |
| 65 | } |
| 66 | |
| 67 | // Open (or create) the lock file and acquire an exclusive lock. |
| 68 | let lock_path = dir.join(LOCK_FILENAME); |
| 69 | let lock_file = match std::fs::OpenOptions::new() |
| 70 | .create(true) |
| 71 | .write(true) |
| 72 | .truncate(false) |
| 73 | .open(&lock_path) |
| 74 | { |
| 75 | Ok(f) => f, |
| 76 | Err(e) => { |
| 77 | log::warn!("Failed to open lock file for session {}: {}", session_id, e,); |
| 78 | return None; |
| 79 | } |
| 80 | }; |
| 81 | |
| 82 | if let Err(e) = lock_file.try_lock_exclusive() { |
| 83 | if e.kind() == std::io::ErrorKind::WouldBlock { |
| 84 | log::warn!( |
| 85 | "Provenance accumulator for session {} is already locked; skipping best-effort provenance update", |
| 86 | session_id, |
| 87 | ); |
| 88 | } else { |
| 89 | log::warn!("Failed to acquire lock for session {}: {}", session_id, e,); |
| 90 | } |
| 91 | return None; |
| 92 | } |
| 93 | |
| 94 | // Load (or create) the accumulator while holding the lock. |
| 95 | let mut acc = match ProvenanceAccumulator::load_or_create(&dir, session_id) { |
| 96 | Ok(a) => a, |
| 97 | Err(e) => { |
| 98 | log::warn!( |
| 99 | "Failed to load provenance accumulator for {}: {}", |
| 100 | session_id, |
| 101 | e, |
| 102 | ); |
| 103 | let _ = lock_file.unlock(); |
| 104 | return None; |
| 105 | } |
| 106 | }; |
| 107 | |
| 108 | // Run the callback. |
| 109 | let should_save = f(&mut acc); |
| 110 |
no test coverage detected