1use std::ops::ControlFlow;
4use std::sync::{Arc, Mutex, MutexGuard};
5
6use crate::error::Result;
7
8type StoreResult<T> = std::result::Result<T, RefStoreError>;
9use crate::reflog::ReflogEntry;
10
11use super::apply::{rollback_failed_apply, snapshot_for_apply_rollback};
12use super::backend::{
13 apply_transaction, check_transaction, partition_updates_allowing_failures, resolve_symbolic,
14 RefBatchState,
15};
16use super::error::RefStoreError;
17use super::transaction::RefTransaction;
18use super::{PreparedRefTransaction, RawRef, RefEntry, RefStorageFormat, RefStore};
19
20#[derive(Debug, Clone)]
21struct MemoryRefStoreInner {
22 state: Arc<RefBatchState>,
25 prepared: Option<RefTransaction>,
27 rejections: Vec<(String, RefStoreError)>,
29}
30
31impl MemoryRefStoreInner {
32 fn state_mut(&mut self) -> &mut RefBatchState {
33 Arc::make_mut(&mut self.state)
34 }
35}
36
37#[derive(Debug)]
44pub struct MemoryRefStore {
45 inner: Mutex<MemoryRefStoreInner>,
46}
47
48impl Default for MemoryRefStore {
49 fn default() -> Self {
50 Self::new()
51 }
52}
53
54impl MemoryRefStore {
55 #[must_use]
57 pub fn new() -> Self {
58 Self {
59 inner: Mutex::new(MemoryRefStoreInner {
60 state: Arc::default(),
61 prepared: None,
62 rejections: Vec::new(),
63 }),
64 }
65 }
66
67 pub fn set_ref(&self, name: impl Into<String>, value: RawRef) -> StoreResult<()> {
73 let name = name.into();
74 let mut inner = self.lock();
75 if inner.prepared.is_some() {
76 return Err(RefStoreError::LockHeld { name });
77 }
78 inner.state_mut().refs.insert(name, value);
79 Ok(())
80 }
81
82 fn lock(&self) -> MutexGuard<'_, MemoryRefStoreInner> {
83 self.inner.lock().unwrap_or_else(|e| e.into_inner())
84 }
85
86 fn snapshot(&self) -> Arc<RefBatchState> {
88 Arc::clone(&self.lock().state)
89 }
90}
91
92impl RefStore for MemoryRefStore {
93 fn format(&self) -> RefStorageFormat {
94 RefStorageFormat::Memory
95 }
96
97 fn read_raw(&self, name: &str) -> Result<Option<RawRef>> {
98 Ok(self.lock().state.refs.get(name).cloned())
99 }
100
101 fn for_each_ref(
102 &self,
103 prefix: &str,
104 f: &mut dyn FnMut(&RefEntry) -> ControlFlow<()>,
105 ) -> Result<()> {
106 let prefix = if prefix.is_empty() { "refs/" } else { prefix };
107 let state = self.snapshot();
108 for (name, value) in state.refs.range(prefix.to_owned()..) {
109 if !name.starts_with(prefix) {
110 break;
111 }
112 let peeled = match &value {
113 RawRef::Direct(_) => None,
114 RawRef::Symbolic(_) => resolve_symbolic(&state.refs, name).ok(),
115 };
116 let entry = RefEntry {
117 name: name.clone(),
118 value: value.clone(),
119 peeled,
120 };
121 if f(&entry).is_break() {
122 break;
123 }
124 }
125 Ok(())
126 }
127
128 fn prepare(
129 &self,
130 txn: RefTransaction,
131 ) -> StoreResult<Box<dyn PreparedRefTransaction + Send + '_>> {
132 txn.validate()?;
133 let mut inner = self.lock();
134 if inner.prepared.is_some() {
135 return Err(RefStoreError::LockHeld {
136 name: txn.lock_name(),
137 });
138 }
139
140 let allow_failures = txn.allows_failures();
141 let txn = if allow_failures {
142 let (updates, reflog_renames, case_only) = {
143 let updates = txn.updates().to_vec();
144 let reflog_renames = txn.reflog_renames().to_vec();
145 let case_only = txn.case_only_renames().to_vec();
146 (updates, reflog_renames, case_only)
147 };
148 let (accepted, rejected) = partition_updates_allowing_failures(&inner.state, updates)?;
149 inner.rejections = rejected;
150 let mut accepted_txn = RefTransaction::from_updates(accepted)?;
151 for rename in reflog_renames {
152 accepted_txn = accepted_txn.with_reflog_rename(rename);
153 }
154 for rename in case_only {
155 accepted_txn = accepted_txn.with_case_only_rename(rename);
156 }
157 accepted_txn
158 } else {
159 inner.rejections.clear();
160 txn
161 };
162
163 check_transaction(&inner.state, &txn)?;
164 let rejections = inner.rejections.clone();
165 inner.prepared = Some(txn);
166
167 Ok(Box::new(MemoryPrepared {
168 store: self,
169 committed: false,
170 rejections,
171 }))
172 }
173
174 fn reflog_exists(&self, name: &str) -> Result<bool> {
175 Ok(self.lock().state.reflogs.contains_key(name))
176 }
177
178 fn for_each_reflog_entry(
179 &self,
180 name: &str,
181 reverse: bool,
182 f: &mut dyn FnMut(&ReflogEntry) -> ControlFlow<()>,
183 ) -> Result<()> {
184 let state = self.snapshot();
185 let Some(entries) = state.reflogs.get(name) else {
186 return Ok(());
187 };
188 if reverse {
189 for entry in entries.iter().rev() {
190 if f(entry).is_break() {
191 break;
192 }
193 }
194 } else {
195 for entry in entries {
196 if f(entry).is_break() {
197 break;
198 }
199 }
200 }
201 Ok(())
202 }
203
204 fn create_reflog(&self, name: &str) -> Result<()> {
205 let mut inner = self.lock();
206 if !inner.state.reflogs.contains_key(name) {
207 inner
208 .state_mut()
209 .reflogs
210 .insert(name.to_owned(), Vec::new());
211 }
212 Ok(())
213 }
214
215 fn delete_reflog(&self, name: &str) -> Result<()> {
216 let mut inner = self.lock();
217 if inner.state.reflogs.contains_key(name) {
218 inner.state_mut().reflogs.remove(name);
219 }
220 Ok(())
221 }
222
223 fn replace_reflog(&self, name: &str, entries: Vec<ReflogEntry>) -> Result<()> {
224 self.rewrite_reflog(name, &mut |_| Ok(entries.clone()))
225 }
226
227 fn rewrite_reflog(
228 &self,
229 name: &str,
230 transform: &mut dyn FnMut(Vec<ReflogEntry>) -> Result<Vec<ReflogEntry>>,
231 ) -> Result<()> {
232 let mut inner = self.lock();
233 let current = inner.state.reflogs.get(name).cloned().unwrap_or_default();
234 let rewritten = transform(current)?;
235 inner.state_mut().reflogs.insert(name.to_owned(), rewritten);
236 Ok(())
237 }
238
239 fn for_each_reflog_ref(&self, f: &mut dyn FnMut(&str) -> ControlFlow<()>) -> Result<()> {
240 let state = self.snapshot();
241 for name in state.reflogs.keys() {
242 if f(name).is_break() {
243 break;
244 }
245 }
246 Ok(())
247 }
248}
249
250struct MemoryPrepared<'a> {
251 store: &'a MemoryRefStore,
252 committed: bool,
253 rejections: Vec<(String, RefStoreError)>,
254}
255
256impl PreparedRefTransaction for MemoryPrepared<'_> {
257 fn rejected_updates(&self) -> &[(String, RefStoreError)] {
258 &self.rejections
259 }
260
261 fn commit(mut self: Box<Self>) -> StoreResult<()> {
262 let mut inner = self.store.lock();
263 let Some(txn) = inner.prepared.take() else {
264 return Ok(());
265 };
266 let backup = snapshot_for_apply_rollback(&inner.state, &txn);
267 let state = inner.state_mut();
268 if let Err(err) = apply_transaction(state, &txn) {
269 rollback_failed_apply(state, &backup);
270 return Err(err);
271 }
272 self.committed = true;
273 Ok(())
274 }
275
276 fn abort(mut self: Box<Self>) -> StoreResult<()> {
277 let mut inner = self.store.lock();
278 inner.prepared = None;
279 self.committed = true;
280 Ok(())
281 }
282}
283
284impl Drop for MemoryPrepared<'_> {
285 fn drop(&mut self) {
286 if self.committed {
287 return;
288 }
289 let mut inner = self.store.inner.lock().unwrap_or_else(|e| e.into_inner());
290 inner.prepared = None;
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297 use crate::objects::ObjectId;
298
299 fn oid(byte: u8) -> ObjectId {
300 let mut bytes = [0u8; 20];
301 bytes[19] = byte;
302 ObjectId::from_bytes(&bytes).expect("valid oid")
303 }
304
305 #[test]
306 fn for_each_ref_peels_symbolic_without_deadlock() {
307 let store = MemoryRefStore::new();
308 store
309 .set_ref("refs/heads/main", RawRef::Direct(oid(1)))
310 .expect("main");
311 store
312 .set_ref("HEAD", RawRef::Symbolic("refs/heads/main".to_owned()))
313 .expect("head");
314
315 let mut peeled = None;
316 store
317 .for_each_ref("HEAD", &mut |entry| {
318 peeled = entry.peeled;
319 ControlFlow::Break(())
320 })
321 .expect("iter");
322 assert_eq!(peeled, Some(oid(1)));
323 }
324
325 fn finishes_promptly(body: impl FnOnce() + Send + 'static) {
328 let (done_tx, done_rx) = std::sync::mpsc::channel();
329 let handle = std::thread::spawn(move || {
330 body();
331 let _ = done_tx.send(());
332 });
333 match done_rx.recv_timeout(std::time::Duration::from_secs(10)) {
334 Ok(()) => handle.join().expect("test thread"),
335 Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => {
336 if let Err(panic) = handle.join() {
337 std::panic::resume_unwind(panic);
338 }
339 }
340 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
341 panic!("callback re-entering the store deadlocked")
342 }
343 }
344 }
345
346 fn sample_reflog(byte: u8) -> Vec<ReflogEntry> {
347 vec![ReflogEntry {
348 old_oid: oid(0),
349 new_oid: oid(byte),
350 identity: "A <a@example.com> 1 +0000".to_owned(),
351 message: format!("m{byte}"),
352 }]
353 }
354
355 #[test]
356 fn for_each_ref_callback_can_read_the_store() {
357 finishes_promptly(|| {
358 let store = MemoryRefStore::new();
359 store
360 .set_ref("refs/heads/main", RawRef::Direct(oid(1)))
361 .expect("main");
362 store
363 .set_ref(
364 "refs/heads/alias",
365 RawRef::Symbolic("refs/heads/main".into()),
366 )
367 .expect("alias");
368
369 let mut seen = Vec::new();
370 store
371 .for_each_ref("refs/", &mut |entry| {
372 let raw = store.read_raw(&entry.name).expect("read_raw");
373 let resolved = store.resolve(&entry.name).expect("resolve");
374 let mut nested = 0usize;
375 store
376 .for_each_ref("refs/heads/", &mut |_| {
377 nested += 1;
378 ControlFlow::Continue(())
379 })
380 .expect("nested for_each_ref");
381 seen.push((entry.name.clone(), raw, resolved, nested));
382 ControlFlow::Continue(())
383 })
384 .expect("for_each_ref");
385 assert_eq!(
386 seen,
387 vec![
388 (
389 "refs/heads/alias".to_owned(),
390 Some(RawRef::Symbolic("refs/heads/main".into())),
391 oid(1),
392 2,
393 ),
394 (
395 "refs/heads/main".to_owned(),
396 Some(RawRef::Direct(oid(1))),
397 oid(1),
398 2,
399 ),
400 ]
401 );
402 });
403 }
404
405 #[test]
406 fn for_each_reflog_ref_callback_can_read_each_reflog() {
407 finishes_promptly(|| {
408 let store = MemoryRefStore::new();
409 store
410 .replace_reflog("HEAD", sample_reflog(1))
411 .expect("HEAD log");
412 store
413 .replace_reflog("refs/heads/main", sample_reflog(2))
414 .expect("main log");
415
416 let mut dump = Vec::new();
417 store
418 .for_each_reflog_ref(&mut |name| {
419 assert!(store.reflog_exists(name).expect("reflog_exists"));
420 store
421 .for_each_reflog_entry(name, false, &mut |entry| {
422 assert!(store.read_raw(name).expect("read_raw").is_none());
423 dump.push((name.to_owned(), entry.new_oid));
424 ControlFlow::Continue(())
425 })
426 .expect("for_each_reflog_entry");
427 ControlFlow::Continue(())
428 })
429 .expect("for_each_reflog_ref");
430 assert_eq!(
431 dump,
432 vec![
433 ("HEAD".to_owned(), oid(1)),
434 ("refs/heads/main".to_owned(), oid(2)),
435 ]
436 );
437 });
438 }
439
440 #[test]
441 fn writes_from_callbacks_succeed_without_changing_the_walk() {
442 finishes_promptly(|| {
443 let store = MemoryRefStore::new();
444 store
445 .set_ref("refs/heads/a", RawRef::Direct(oid(1)))
446 .expect("a");
447 store
448 .set_ref("refs/heads/b", RawRef::Direct(oid(2)))
449 .expect("b");
450 store
451 .replace_reflog("refs/heads/a", sample_reflog(1))
452 .expect("log");
453
454 let mut names = Vec::new();
455 store
456 .for_each_ref("refs/heads/", &mut |entry| {
457 store
458 .set_ref("refs/heads/added", RawRef::Direct(oid(3)))
459 .expect("set_ref from callback");
460 names.push(entry.name.clone());
461 ControlFlow::Continue(())
462 })
463 .expect("for_each_ref");
464 assert_eq!(names, vec!["refs/heads/a", "refs/heads/b"]);
465 assert_eq!(
466 store.read_raw("refs/heads/added").expect("read"),
467 Some(RawRef::Direct(oid(3)))
468 );
469
470 let mut entries = 0usize;
471 store
472 .for_each_reflog_entry("refs/heads/a", true, &mut |_| {
473 store
474 .replace_reflog("refs/heads/a", Vec::new())
475 .expect("replace_reflog from callback");
476 entries += 1;
477 ControlFlow::Continue(())
478 })
479 .expect("for_each_reflog_entry");
480 assert_eq!(entries, 1);
481
482 let mut logged = Vec::new();
483 store
484 .for_each_reflog_ref(&mut |name| {
485 store
486 .delete_reflog(name)
487 .expect("delete_reflog from callback");
488 store.create_reflog("refs/heads/zz").expect("create_reflog");
489 logged.push(name.to_owned());
490 ControlFlow::Continue(())
491 })
492 .expect("for_each_reflog_ref");
493 assert_eq!(logged, vec!["refs/heads/a"]);
494 assert!(!store.reflog_exists("refs/heads/a").expect("exists"));
495 assert!(store.reflog_exists("refs/heads/zz").expect("exists"));
496 });
497 }
498}