diff --git a/Cargo.toml b/Cargo.toml index 712fdeb6..f70f184f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,16 @@ azure-devops = { project = "jonhoo/jonhoo", pipeline = "flurry", build = "15" } codecov = { repository = "jonhoo/flurry", branch = "master", service = "github" } maintenance = { status = "experimental" } +[features] +sanitize = ['crossbeam-epoch/sanitize'] + [dependencies] -crossbeam-epoch = "0.8" +crossbeam-epoch = "0.9" parking_lot = "0.10" num_cpus = "1.12.0" [dev-dependencies] rand = "0.7" + +[patch.crates-io] +crossbeam-epoch = { git = "https://github.com/cynecx/crossbeam.git", branch = "fix-unsoundness" } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d81fee12..a0e2d621 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,6 +29,57 @@ jobs: condition: ne(variables.CACHE_RESTORED, 'true') - script: cargo deny check displayName: cargo deny + - job: miri + displayName: "Run miri on test suite" + dependsOn: deny + continueOnError: true # since miri sometimes isn't on nightly + pool: + vmImage: ubuntu-16.04 + steps: + - template: install-rust.yml@templates + parameters: + rust: nightly + components: + - miri + # ignore leaks due to https://github.com/crossbeam-rs/crossbeam/issues/464 + - bash: yes | cargo miri -Zmiri-ignore-leaks test + displayName: cargo miri test + - job: asan + dependsOn: deny + displayName: "Run address sanitizer on test suite" + pool: + vmImage: ubuntu-16.04 + steps: + - template: install-rust.yml@templates + parameters: + rust: nightly + - bash: | + sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer + sed -i '/\[features\]/i [profile.dev]' Cargo.toml + sed -i '/profile.dev/a opt-level = 1' Cargo.toml + cat Cargo.toml + displayName: Enable debug symbols + - script: | + env ASAN_OPTIONS="detect_odr_violation=0" RUSTFLAGS="-Z sanitizer=address" cargo test --features sanitize --target x86_64-unknown-linux-gnu + displayName: cargo -Z sanitizer=address test + - job: lsan + dependsOn: deny + displayName: "Run leak sanitizer on test suite" + pool: + vmImage: ubuntu-16.04 + steps: + - template: install-rust.yml@templates + parameters: + rust: nightly + - bash: | + sudo ln -s /usr/bin/llvm-symbolizer-6.0 /usr/bin/llvm-symbolizer + sed -i '/\[features\]/i [profile.dev]' Cargo.toml + sed -i '/profile.dev/a opt-level = 1' Cargo.toml + cat Cargo.toml + displayName: Enable debug symbols + - script: | + env RUSTFLAGS="-Z sanitizer=leak" cargo test --features sanitize --target x86_64-unknown-linux-gnu + displayName: cargo -Z sanitizer=leak test resources: repositories: diff --git a/src/map.rs b/src/map.rs index 24793b47..b428f4e5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1176,6 +1176,9 @@ where // just remove the node if the value is the one we expected at method call if observed_value.map(|ov| ov == ev).unwrap_or(true) { + // we remember the old value so that we can return it and mark it for deletion below + old_val = Some(ev); + // found the node but we have a new value to replace the old one if let Some(nv) = new_value { n.value.store(Owned::new(nv), Ordering::SeqCst); @@ -1183,8 +1186,6 @@ where // so we stop iterating here break; } - // we remember the old value so that we can return it and mark it for deletion below - old_val = Some(ev); // remove the BinEntry containing the removed key value pair from the bucket if !pred.is_null() { // either by changing the pointer of the previous BinEntry, if present @@ -1512,6 +1513,7 @@ where } } +#[cfg(not(miri))] #[inline] /// Returns the number of physical CPUs in the machine (_O(1)_). fn num_cpus() -> usize { @@ -1520,6 +1522,12 @@ fn num_cpus() -> usize { NCPU.load(Ordering::Relaxed) } +#[cfg(miri)] +#[inline] +const fn num_cpus() -> usize { + 1 +} + #[test] fn capacity() { let map = HashMap::::new(); @@ -1694,7 +1702,7 @@ fn replace_existing() { let guard = epoch::pin(); map.insert(42, 42, &guard); let old = map.replace_node(&42, Some(10), None, &guard); - assert!(old.is_none()); + assert_eq!(old, Some(&42)); assert_eq!(*map.get(&42, &guard).unwrap(), 10); } } @@ -1707,7 +1715,7 @@ fn replace_existing_observed_value_matching() { map.insert(42, 42, &guard); let observed_value = Shared::from(map.get(&42, &guard).unwrap() as *const _); let old = map.replace_node(&42, Some(10), Some(observed_value), &guard); - assert!(old.is_none()); + assert_eq!(old, Some(&42)); assert_eq!(*map.get(&42, &guard).unwrap(), 10); } }