Skip to content

Commit a891315

Browse files
committed
fix the first race-condition around initialization in ODB (#301)
1 parent be5a19e commit a891315

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

git-odb/src/store_impls/dynamic/load_index.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl super::Store {
7070
) -> Result<Option<Snapshot>, Error> {
7171
let index = self.index.load();
7272
if !index.is_initialized() {
73-
return self.consolidate_with_disk_state(false /*load one new index*/);
73+
return self.consolidate_with_disk_state(true /* needs_init */, false /*load one new index*/);
7474
}
7575

7676
if marker.generation != index.generation || marker.state_id != index.state_id() {
@@ -86,7 +86,7 @@ impl super::Store {
8686
match refresh_mode {
8787
RefreshMode::Never => Ok(None),
8888
RefreshMode::AfterAllIndicesLoaded => {
89-
self.consolidate_with_disk_state(true /*load one new index*/)
89+
self.consolidate_with_disk_state(false /* needs init */, true /*load one new index*/)
9090
}
9191
}
9292
}
@@ -166,7 +166,11 @@ impl super::Store {
166166

167167
/// refresh and possibly clear out our existing data structures, causing all pack ids to be invalidated.
168168
/// `load_new_index` is an optimization to at least provide one newly loaded pack after refreshing the slot map.
169-
pub(crate) fn consolidate_with_disk_state(&self, load_new_index: bool) -> Result<Option<Snapshot>, Error> {
169+
pub(crate) fn consolidate_with_disk_state(
170+
&self,
171+
needs_init: bool,
172+
load_new_index: bool,
173+
) -> Result<Option<Snapshot>, Error> {
170174
let index = self.index.load();
171175
let previous_index_state = Arc::as_ptr(&index) as usize;
172176

@@ -182,6 +186,15 @@ impl super::Store {
182186
}
183187

184188
let was_uninitialized = !index.is_initialized();
189+
190+
// We might not be able to detect by pointer if the state changed, as this itself is racy. So we keep track of double-initialization
191+
// using a flag, which means that if `needs_init` was true we saw the index uninitialized once, but now that we are here it's
192+
// initialized meaning that somebody was faster and we couldn't detect it by comparisons to the index.
193+
// If so, make sure we collect the snapshot instead of returning None in case nothing actually changed, which is likely with a
194+
// race like this.
195+
if !was_uninitialized && needs_init {
196+
return Ok(Some(self.collect_snapshot()));
197+
}
185198
self.num_disk_state_consolidation.fetch_add(1, Ordering::Relaxed);
186199

187200
let db_paths: Vec<_> = std::iter::once(objects_directory.to_owned())

git-odb/src/store_impls/dynamic/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub mod structure {
136136
pub fn structure(&self) -> Result<Vec<Record>, load_index::Error> {
137137
let index = self.index.load();
138138
if !index.is_initialized() {
139-
self.consolidate_with_disk_state(false /*load one new index*/)?;
139+
self.consolidate_with_disk_state(true, false /*load one new index*/)?;
140140
}
141141
let index = self.index.load();
142142
let mut res: Vec<_> = index

git-odb/src/store_impls/dynamic/verify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl super::Store {
101101
{
102102
let mut index = self.index.load();
103103
if !index.is_initialized() {
104-
self.consolidate_with_disk_state(false)?;
104+
self.consolidate_with_disk_state(true, false)?;
105105
index = self.index.load();
106106
assert!(
107107
index.is_initialized(),

0 commit comments

Comments
 (0)