Skip to content

Commit

Permalink
1) Added get_or_insert_with method for SkipMap too
Browse files Browse the repository at this point in the history
2) Added corresponding test case for SkipMap
  • Loading branch information
snow01 committed Jul 22, 2021
1 parent 2027c1b commit 403e899
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 3 deletions.
4 changes: 2 additions & 2 deletions crossbeam-skiplist/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,8 +476,8 @@ where

/// Finds an entry with the specified key, or inserts a new `key`-`value` pair if none exist,
/// where value is calculated with a function.
/// <br>
/// <br>
///
///
/// <b>Note:</b> Another thread may write key value first, leading to the result of this closure
/// discarded. If closure is modifying some other state (such as shared counters or shared
/// objects), it may lead to <u>undesired behaviour</u> such as counters being changed without
Expand Down
33 changes: 33 additions & 0 deletions crossbeam-skiplist/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,39 @@ where
Entry::new(self.inner.get_or_insert(key, value, guard))
}

/// Finds an entry with the specified key, or inserts a new `key`-`value` pair if none exist,
/// where value is calculated with a function.
///
///
/// <b>Note:</b> Another thread may write key value first, leading to the result of this closure
/// discarded. If closure is modifying some other state (such as shared counters or shared
/// objects), it may lead to <u>undesired behaviour</u> such as counters being changed without
/// result of closure inserted
////
/// This function returns an [`Entry`] which
/// can be used to access the key's associated value.
///
///
/// # Example
/// ```
/// use crossbeam_skiplist::SkipMap;
///
/// let ages = SkipMap::new();
/// let gates_age = ages.get_or_insert_with("Bill Gates", || 64);
/// assert_eq!(*gates_age.value(), 64);
///
/// ages.insert("Steve Jobs", 65);
/// let jobs_age = ages.get_or_insert_with("Steve Jobs", || -1);
/// assert_eq!(*jobs_age.value(), 65);
/// ```
pub fn get_or_insert_with<F>(&self, key: K, value_fn: F) -> Entry<'_, K, V>
where
F: FnOnce() -> V,
{
let guard = &epoch::pin();
Entry::new(self.inner.get_or_insert_with(key, value_fn, guard))
}

/// Returns an iterator over all entries in the map,
/// sorted by key.
///
Expand Down
2 changes: 1 addition & 1 deletion crossbeam-skiplist/tests/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ fn get_or_insert_with_parallel_run() {
},
guard,
)
.value(),
.value(),
700
);
});
Expand Down
61 changes: 61 additions & 0 deletions crossbeam-skiplist/tests/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,67 @@ fn get_or_insert() {
assert_eq!(*s.get_or_insert(6, 600).value(), 600);
}

#[test]
fn get_or_insert_with() {
let s = SkipMap::new();
s.insert(3, 3);
s.insert(5, 5);
s.insert(1, 1);
s.insert(4, 4);
s.insert(2, 2);

assert_eq!(*s.get(&4).unwrap().value(), 4);
assert_eq!(*s.insert(4, 40).value(), 40);
assert_eq!(*s.get(&4).unwrap().value(), 40);

assert_eq!(*s.get_or_insert_with(4, || 400).value(), 40);
assert_eq!(*s.get(&4).unwrap().value(), 40);
assert_eq!(*s.get_or_insert_with(6, || 600).value(), 600);
}

#[test]
fn get_or_insert_with_panic() {
use std::panic;

let s = SkipMap::new();
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
s.get_or_insert_with(4, || panic!());
}));
assert!(res.is_err());
assert!(s.is_empty());
assert_eq!(*s.get_or_insert_with(4, || 40).value(), 40);
assert_eq!(s.len(), 1);
}

#[test]
fn get_or_insert_with_parallel_run() {
use std::sync::{Arc, Mutex};

let s = Arc::new(SkipMap::new());
let s2 = s.clone();
let called = Arc::new(Mutex::new(false));
let called2 = called.clone();
let handle = std::thread::spawn(move || {
assert_eq!(
*s2.get_or_insert_with(7, || {
*called2.lock().unwrap() = true;

// allow main thread to run before we return result
std::thread::sleep(std::time::Duration::from_secs(4));
70
})
.value(),
700
);
});
std::thread::sleep(std::time::Duration::from_secs(2));

// main thread writes the value first
assert_eq!(*s.get_or_insert(7, 700).value(), 700);
handle.join().unwrap();
assert!(*called.lock().unwrap());
}

#[test]
fn get_next_prev() {
let s = SkipMap::new();
Expand Down

0 comments on commit 403e899

Please sign in to comment.