mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 23:07:18 +03:00
treestate: add tests about concurrent writes
Summary: Treestate can be corrupted by concurrent writes. This is used to verify the fix in a later diff. Reviewed By: zzl0 Differential Revision: D42492671 fbshipit-source-id: b5ede9f66e85cef4b5bfd7ae1732ab04f258b13b
This commit is contained in:
parent
20253392b0
commit
611dcf2ad7
@ -52,6 +52,7 @@ bitflags! {
|
||||
/// | removed | either one is yes | no | ? |
|
||||
/// | untracked | no | no | no | no |
|
||||
/// | ignored | no | no | no | yes |
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
pub struct StateFlags: u16 {
|
||||
/// Exist in the first working parent.
|
||||
const EXIST_P1 = 1;
|
||||
@ -91,6 +92,7 @@ impl StateFlags {
|
||||
/// Unlike V1, the `state` field is no longer a char defined by Mercurial,
|
||||
/// but a bitflag. It also has a `copied` field.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(test, derive(Default))]
|
||||
pub struct FileStateV2 {
|
||||
/// Mode (permissions) mask for the file.
|
||||
pub mode: u32,
|
||||
|
@ -903,4 +903,69 @@ mod tests {
|
||||
[p1, p3].to_vec()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic] // BUG
|
||||
fn test_concurrent_writes() {
|
||||
check_concurrent_writes(&[b"a"], &[b"b"]);
|
||||
check_concurrent_writes(&[b"a/1"], &[b"a/2"]);
|
||||
|
||||
let paths1 = SAMPLE_PATHS.into_iter().take(10).collect::<Vec<_>>();
|
||||
let paths2 = SAMPLE_PATHS.into_iter().rev().take(1).collect::<Vec<_>>();
|
||||
check_concurrent_writes(&paths1, &paths2);
|
||||
|
||||
let paths2 = SAMPLE_PATHS.into_iter().rev().take(10).collect::<Vec<_>>();
|
||||
check_concurrent_writes(&paths1, &paths2);
|
||||
}
|
||||
|
||||
// Test appending paths1, and paths2 "concurrently".
|
||||
// Paths should not overlap.
|
||||
fn check_concurrent_writes(paths1: &[&[u8]], paths2: &[&[u8]]) {
|
||||
let dir = tempdir().unwrap();
|
||||
let dir_path = dir.path();
|
||||
|
||||
// Prepare initial state.
|
||||
let (path, root_id) = {
|
||||
let mut state = new_treestate(dir_path);
|
||||
let root_id = state.flush().unwrap();
|
||||
(dir_path.join(state.file_name().unwrap()), root_id)
|
||||
};
|
||||
|
||||
// Concurrent writes.
|
||||
let mut state1 = TreeState::open(&path, root_id, true).unwrap();
|
||||
let mut state2 = TreeState::open(&path, root_id, true).unwrap();
|
||||
let file_state1: FileStateV2 = Default::default();
|
||||
let file_state2 = FileStateV2 {
|
||||
size: file_state1.size + 1,
|
||||
..file_state1.clone()
|
||||
};
|
||||
for p in paths1 {
|
||||
state1.insert(p, &file_state1).unwrap();
|
||||
}
|
||||
for p in paths2 {
|
||||
state2.insert(p, &file_state2).unwrap();
|
||||
}
|
||||
|
||||
let root_id1 = state1.flush().unwrap();
|
||||
|
||||
// Panic (debug build) at
|
||||
// debug_assert!(self.position == file.seek(SeekFrom::End(0))?);
|
||||
// in filestore.rs
|
||||
//
|
||||
// Might error out with (release build): "invalid store id: ..."
|
||||
let root_id2 = state2.flush().unwrap();
|
||||
|
||||
// Check that things can be read properly (aka. no "invalid store id: ..." errors),
|
||||
// and are written properly (file_state1 and file_state2).
|
||||
let mut state1 = TreeState::open(&path, root_id1, true).unwrap();
|
||||
let mut state2 = TreeState::open(&path, root_id2, true).unwrap();
|
||||
for p in paths1 {
|
||||
let got = state1.get(p).unwrap().unwrap();
|
||||
assert_eq!(got, &file_state1);
|
||||
}
|
||||
for p in paths2 {
|
||||
let got = state2.get(p).unwrap().unwrap();
|
||||
assert_eq!(got, &file_state2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user