use crate::guard::TryInsertError;
use crate::lockable_map_impl::{LockableMapConfig, LockableMapImpl};
use std::borrow::Borrow;
use std::fmt::Debug;
use std::hash::Hash;
use std::time::{Duration, Instant};
pub(crate) fn assert_vec_eq_unordered<T: Ord + Eq + Debug>(mut lhs: Vec<T>, mut rhs: Vec<T>) {
lhs.sort();
rhs.sort();
assert_eq!(lhs, rhs);
}
pub(crate) fn wait_for(mut func: impl FnMut() -> bool, timeout: Duration) {
let start = Instant::now();
while !func() {
if start.elapsed() > timeout {
panic!("Timeout waiting for condition");
}
std::thread::sleep(Duration::from_millis(1));
}
}
pub(crate) async fn wait_for_async(mut func: impl FnMut() -> bool, timeout: Duration) {
let start = Instant::now();
while !func() {
if start.elapsed() > timeout {
panic!("Timeout waiting for condition");
}
tokio::time::sleep(Duration::from_millis(1)).await;
}
}
pub(crate) trait Guard<K, V> {
fn key(&self) -> &K;
fn value(&self) -> Option<&V>;
fn value_mut(&mut self) -> Option<&mut V>;
fn value_or_insert(&mut self, value: V) -> &mut V;
fn value_or_insert_with(&mut self, value_fn: impl FnOnce() -> V) -> &mut V;
fn insert(&mut self, value: V) -> Option<V>;
fn try_insert(&mut self, value: V) -> Result<&mut V, TryInsertError<V>>;
fn remove(&mut self) -> Option<V>;
}
impl<K, V, C, P> Guard<K, V> for crate::guard::Guard<K, V, C, P>
where
K: Eq + PartialEq + Hash + Clone,
C: LockableMapConfig + Clone,
P: Borrow<LockableMapImpl<K, V, C>>,
{
fn key(&self) -> &K {
crate::guard::Guard::key(self)
}
fn value(&self) -> Option<&V> {
crate::guard::Guard::value(self)
}
fn value_mut(&mut self) -> Option<&mut V> {
crate::guard::Guard::value_mut(self)
}
fn value_or_insert_with(&mut self, value_fn: impl FnOnce() -> V) -> &mut V {
crate::guard::Guard::value_or_insert_with(self, value_fn)
}
fn value_or_insert(&mut self, value: V) -> &mut V {
crate::guard::Guard::value_or_insert(self, value)
}
fn insert(&mut self, value: V) -> Option<V> {
crate::guard::Guard::insert(self, value)
}
fn try_insert(&mut self, value: V) -> Result<&mut V, TryInsertError<V>> {
crate::guard::Guard::try_insert(self, value)
}
fn remove(&mut self) -> Option<V> {
crate::guard::Guard::remove(self)
}
}
#[macro_export]
macro_rules! instantiate_lockable_tests {
(@gen_tests, $lockable_type: ident, $test_sync_fn: ident, $test_async_fn: ident) => {
#[tokio::test]
async fn async_lock() {
$test_async_fn(AsyncLock).await;
}
#[tokio::test]
async fn async_lock_owned() {
$test_async_fn(AsyncLockOwned).await;
}
#[tokio::test]
async fn try_lock_async() {
$test_async_fn(TryLockAsync).await;
}
#[tokio::test]
async fn try_lock_owned_async() {
$test_async_fn(TryLockOwnedAsync).await;
}
#[test]
fn blocking_lock() {
$test_sync_fn(BlockingLock);
}
#[test]
fn blocking_lock_owned() {
$test_sync_fn(BlockingLockOwned);
}
#[test]
fn try_lock() {
$test_sync_fn(TryLock);
}
#[test]
fn try_lock_owned() {
$test_sync_fn(TryLockOwned);
}
};
($lockable_type: ident) => {
use std::ops::Deref;
use std::sync::Arc;
use tokio::sync::{Mutex, OwnedMutexGuard};
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};
use futures::{stream::StreamExt};
use $crate::{Lockable, InfallibleUnwrap, TryInsertError, tests::Guard, utils::stream::MyStreamExt};
trait SyncLocking<S, K, V> : Clone + Send
where
S: Borrow<$lockable_type<K, V>>,
K: Eq + PartialEq + Hash + Clone,
{
type Guard<'a> : $crate::tests::Guard<K, V> where S: 'a;
fn new_lockable(&self) -> S;
fn lock<'a>(&self, map: &'a S, key: K) -> Self::Guard<'a>;
fn lock_waiting_is_ok<'a>(&self, map: &'a S, key: K) -> Self::Guard<'a> {
self.lock(map, key)
}
fn extract(&self, s: S) -> $lockable_type<K, V>;
}
#[derive(Clone, Copy)]
struct BlockingLock;
impl <K, V> SyncLocking<$lockable_type::<K, V>, K, V> for BlockingLock
where
K: Eq + PartialEq + Hash + Clone,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::Guard<'a> where $lockable_type<K, V>: 'a;
fn new_lockable(&self) -> $lockable_type<K, V> {
$lockable_type::<K, V>::new()
}
fn lock<'a>(&self, map: &'a $lockable_type::<K, V>, key: K) -> Self::Guard<'a> {
map.blocking_lock(key, SyncLimit::no_limit()).infallible_unwrap()
}
fn extract(&self, s: $lockable_type<K, V>) -> $lockable_type<K, V> {
s
}
}
#[derive(Clone, Copy)]
struct BlockingLockOwned;
impl <K, V> SyncLocking<Arc<$lockable_type::<K, V>>, K, V> for BlockingLockOwned
where
K: Eq + PartialEq + Hash + Clone + Debug + 'static,
V: Debug + 'static,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::OwnedGuard;
fn new_lockable(&self) -> Arc<$lockable_type::<K, V>> {
Arc::new($lockable_type::<K, V>::new())
}
fn lock<'a>(&self, map: &'a Arc<$lockable_type<K, V>>, key: K) -> Self::Guard<'a> {
map.blocking_lock_owned(key, SyncLimit::no_limit()).infallible_unwrap()
}
fn extract(&self, s: Arc<$lockable_type<K, V>>) -> $lockable_type<K, V> {
Arc::try_unwrap(s).unwrap()
}
}
#[derive(Clone, Copy)]
struct TryLock;
impl <K, V> SyncLocking<$lockable_type::<K, V>, K, V> for TryLock
where
K: Eq + PartialEq + Hash + Clone,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::Guard<'a> where $lockable_type<K, V>: 'a;
fn new_lockable(&self) -> $lockable_type<K, V> {
$lockable_type::<K, V>::new()
}
fn lock<'a>(&self, map: &'a $lockable_type<K, V>, key: K) -> Self::Guard<'a> {
map.try_lock(key, SyncLimit::no_limit()).infallible_unwrap().expect("Entry already locked")
}
fn lock_waiting_is_ok<'a>(&self, map: &'a $lockable_type<K, V>, key: K) -> Self::Guard<'a> {
let start = Instant::now();
loop {
if let Some(guard) = map.try_lock(key.clone(), SyncLimit::no_limit()).infallible_unwrap() {
break guard;
}
if Instant::now() - start > Duration::from_secs(10) {
panic!("Timeout trying to get lock in TryLock::lock_waiting_is_ok");
}
}
}
fn extract(&self, s: $lockable_type<K, V>) -> $lockable_type<K, V> {
s
}
}
#[derive(Clone, Copy)]
struct TryLockOwned;
impl <K, V> SyncLocking<Arc<$lockable_type::<K, V>>, K, V> for TryLockOwned
where
K: Eq + PartialEq + Hash + Clone + Debug + 'static,
V: Debug + 'static,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::OwnedGuard;
fn new_lockable(&self) -> Arc<$lockable_type::<K, V>> {
Arc::new($lockable_type::<K, V>::new())
}
fn lock<'a>(&self, map: &'a Arc<$lockable_type<K, V>>, key: K) -> Self::Guard<'a> {
map.try_lock_owned(key, SyncLimit::no_limit()).infallible_unwrap().expect("Entry already locked")
}
fn lock_waiting_is_ok<'a>(&self, map: &'a Arc<$lockable_type<K, V>>, key: K) -> Self::Guard<'a> {
let start = Instant::now();
loop {
if let Some(guard) = map.try_lock_owned(key.clone(), SyncLimit::no_limit()).infallible_unwrap() {
break guard;
}
if Instant::now() - start > Duration::from_secs(10) {
panic!("Timeout trying to get lock in TryLockOwned::lock_waiting_is_ok");
}
}
}
fn extract(&self, s: Arc<$lockable_type<K, V>>) -> $lockable_type<K, V> {
Arc::try_unwrap(s).unwrap()
}
}
trait AsyncLocking<S, K, V> : Clone + Send
where
S: Borrow<$lockable_type::<K, V>> + Sync,
K: Eq + PartialEq + Hash + Clone + Send + 'static,
{
type Guard<'a> : $crate::tests::Guard<K, V> where S: 'a;
fn new_lockable(&self) -> S;
async fn lock<'a>(&self, map: &'a S, key: K) -> Self::Guard<'a>;
async fn lock_waiting_is_ok<'a>(&self, map: &'a S, key: K) -> Self::Guard<'a> {
self.lock(map, key).await
}
fn extract(&self, s: S) -> $lockable_type<K, V>;
}
#[derive(Clone, Copy)]
struct TryLockAsync;
impl <K, V> AsyncLocking<$lockable_type::<K, V>, K, V> for TryLockAsync
where
K: Eq + PartialEq + Hash + Clone + Send + Sync + 'static,
V: Send + Sync,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::Guard<'a> where $lockable_type<K, V>: 'a;
fn new_lockable(&self) -> $lockable_type<K, V> {
$lockable_type::<K, V>::new()
}
async fn lock<'a>(&self, map: &'a $lockable_type::<K, V>, key: K) -> Self::Guard<'a> {
map.try_lock_async(key, AsyncLimit::no_limit()).await.infallible_unwrap().expect("Entry already locked")
}
async fn lock_waiting_is_ok<'a>(&self, map: &'a $lockable_type::<K, V>, key: K) -> Self::Guard<'a> {
let start = Instant::now();
loop {
if let Some(guard) = map.try_lock_async(key.clone(), AsyncLimit::no_limit()).await.infallible_unwrap() {
break guard;
}
if Instant::now() - start > Duration::from_secs(10) {
panic!("Timeout trying to get lock in TryLockAsync::lock_waiting_is_ok");
}
}
}
fn extract(&self, s: $lockable_type<K, V>) -> $lockable_type<K, V> {
s
}
}
#[derive(Clone, Copy)]
struct TryLockOwnedAsync;
impl <K, V> AsyncLocking<Arc<$lockable_type::<K, V>>, K, V> for TryLockOwnedAsync
where
K: Eq + PartialEq + Hash + Clone + Send + Sync + Debug + 'static,
V: Send + Sync + Debug + 'static,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::OwnedGuard;
fn new_lockable(&self) -> Arc<$lockable_type::<K, V>> {
Arc::new($lockable_type::<K, V>::new())
}
async fn lock<'a>(&self, map: &'a Arc<$lockable_type::<K, V>>, key: K) -> Self::Guard<'a> {
map.try_lock_owned_async(key, AsyncLimit::no_limit()).await.infallible_unwrap().expect("Entry already locked")
}
async fn lock_waiting_is_ok<'a>(&self, map: &'a Arc<$lockable_type<K, V>>, key: K) -> Self::Guard<'a> {
let start = Instant::now();
loop {
if let Some(guard) = map.try_lock_owned_async(key.clone(), AsyncLimit::no_limit()).await.infallible_unwrap() {
break guard;
}
if Instant::now() - start > Duration::from_secs(10) {
panic!("Timeout trying to get lock in TryLockAsync::lock_waiting_is_ok");
}
}
}
fn extract(&self, s: Arc<$lockable_type<K, V>>) -> $lockable_type<K, V> {
Arc::try_unwrap(s).unwrap()
}
}
#[derive(Clone, Copy)]
struct AsyncLock;
impl <K, V> AsyncLocking<$lockable_type::<K, V>, K, V> for AsyncLock
where
K: Eq + PartialEq + Hash + Clone + Send + Sync + 'static,
V: Send + Sync,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::Guard<'a> where $lockable_type<K, V>: 'a;
fn new_lockable(&self) -> $lockable_type<K, V> {
$lockable_type::<K, V>::new()
}
async fn lock<'a>(&self, map: &'a $lockable_type::<K, V>, key: K) -> Self::Guard<'a> {
map.async_lock(key, AsyncLimit::no_limit()).await.infallible_unwrap()
}
fn extract(&self, s: $lockable_type<K, V>) -> $lockable_type<K, V> {
s
}
}
#[derive(Clone, Copy)]
struct AsyncLockOwned;
impl <K, V> AsyncLocking<Arc<$lockable_type::<K, V>>, K, V> for AsyncLockOwned
where
K: Eq + PartialEq + Hash + Clone + Send + Sync + Debug + 'static,
V: Send + Sync + Debug + 'static,
{
type Guard<'a> = <$lockable_type::<K, V> as Lockable<K, V>>::OwnedGuard;
fn new_lockable(&self) -> Arc<$lockable_type::<K, V>> {
Arc::new($lockable_type::<K, V>::new())
}
async fn lock<'a>(&self, map: &'a Arc<$lockable_type<K, V>>, key: K) -> Self::Guard<'a> {
map.async_lock_owned(key, AsyncLimit::no_limit()).await.infallible_unwrap()
}
fn extract(&self, s: Arc<$lockable_type<K, V>>) -> $lockable_type<K, V> {
Arc::try_unwrap(s).unwrap()
}
}
struct LockingThread {
join_handle: Option<JoinHandle<()>>,
acquire_barrier: Arc<Mutex<()>>,
release_barrier_guard: Option<OwnedMutexGuard<()>>,
}
impl LockingThread {
pub fn launch_thread_sync_lock<S, L>(
locking: &L,
pool: &Arc<S>,
key: isize,
) -> Self
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
L: SyncLocking<S, isize, String> + Sync + 'static,
{
Self::launch_thread_sync_lock_with_callback(locking, pool, key, |_| {})
}
pub fn launch_thread_sync_lock_with_callback<S, L>(
locking: &L,
pool: &Arc<S>,
key: isize,
callback: impl FnOnce(&mut L::Guard<'_>) + Send + 'static,
) -> Self
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
L: SyncLocking<S, isize, String> + Sync + 'static,
{
let locking = (*locking).clone();
let pool = Arc::clone(pool);
let acquire_barrier = Arc::new(Mutex::new(()));
let acquire_barrier_guard = Arc::clone(&acquire_barrier).blocking_lock_owned();
let release_barrier = Arc::new(Mutex::new(()));
let release_barrier_guard = Some(Arc::clone(&release_barrier).blocking_lock_owned());
let join_handle = Some(thread::spawn(move || {
let mut guard = locking.lock_waiting_is_ok(&pool, key);
callback(&mut guard);
drop(acquire_barrier_guard);
let _release_barrier = release_barrier.blocking_lock();
}));
Self {
join_handle,
acquire_barrier,
release_barrier_guard,
}
}
pub async fn launch_thread_async_lock<S, L>(
locking: &L,
pool: &Arc<S>,
key: isize,
) -> Self
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
L: AsyncLocking<S, isize, String> + Sync + 'static,
{
Self::launch_thread_async_lock_with_callback(locking, pool, key, |_| {}).await
}
pub async fn launch_thread_async_lock_with_callback<S, L>(
locking: &L,
pool: &Arc<S>,
key: isize,
callback: impl FnOnce(&mut L::Guard<'_>) + Send + 'static,
) -> Self
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
L: AsyncLocking<S, isize, String> + Sync + 'static,
{
let locking = (*locking).clone();
let pool = Arc::clone(pool);
let acquire_barrier = Arc::new(Mutex::new(()));
let acquire_barrier_guard = Arc::clone(&acquire_barrier).lock_owned().await;
let release_barrier = Arc::new(Mutex::new(()));
let release_barrier_guard = Some(Arc::clone(&release_barrier).lock_owned().await);
let join_handle = Some(thread::spawn(move || {
let runtime = tokio::runtime::Runtime::new().unwrap();
let mut guard = runtime.block_on(locking.lock_waiting_is_ok(&pool, key));
callback(&mut guard);
drop(acquire_barrier_guard);
let _release_barrier = release_barrier.blocking_lock();
}));
Self {
join_handle,
acquire_barrier,
release_barrier_guard,
}
}
pub fn entered_lock_section(&self) -> bool {
self.acquire_barrier.try_lock().is_ok()
}
pub fn wait_for_lock(&self) {
$crate::tests::wait_for(|| self.entered_lock_section(), Duration::from_secs(1));
}
pub async fn wait_for_lock_async(&self) {
$crate::tests::wait_for_async(|| self.entered_lock_section(), Duration::from_secs(1)).await;
}
pub fn release(&mut self) {
if let Some(release_barrier_guard) = self.release_barrier_guard.take() {
drop(release_barrier_guard);
}
}
pub fn release_and_wait(mut self) {
self.release();
self.join();
}
pub fn join(&mut self) {
if let Some(join_handle) = self.join_handle.take() {
join_handle.join().unwrap();
}
}
}
impl Drop for LockingThread {
fn drop(&mut self) {
self.release();
self.join();
}
}
#[tokio::test]
#[should_panic(
expected = "Cannot block the current thread from within a runtime. This happens because a function attempted to block the current thread while the thread is being used to drive asynchronous tasks."
)]
async fn blocking_lock_from_async_context_with_sync_api_for_existing_entry() {
let p = $lockable_type::<isize, String>::new();
p.blocking_lock(3, SyncLimit::no_limit()).unwrap().insert(String::new());
let _ = p.blocking_lock(3, SyncLimit::no_limit());
}
#[tokio::test]
async fn blocking_lock_from_async_context_with_sync_api_for_new_entry() {
let p = $lockable_type::<isize, String>::new();
let _ = p.blocking_lock(3, SyncLimit::no_limit());
}
#[tokio::test]
#[should_panic(
expected = "Cannot block the current thread from within a runtime. This happens because a function attempted to block the current thread while the thread is being used to drive asynchronous tasks."
)]
async fn blocking_lock_owned_from_async_context_with_sync_api_for_existing_entry() {
let p = Arc::new($lockable_type::<isize, String>::new());
p.blocking_lock(3, SyncLimit::no_limit()).unwrap().insert(String::new());
let _ = p.blocking_lock_owned(3, SyncLimit::no_limit());
}
#[tokio::test]
async fn blocking_lock_owned_from_async_context_with_sync_api_for_new_entry() {
let p = Arc::new($lockable_type::<isize, String>::new());
let _ = p.blocking_lock_owned(3, SyncLimit::no_limit());
}
mod simple {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let guard = locking.lock(&pool, 4);
assert!(guard.value().is_none());
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let guard = locking.lock(&pool, 4).await;
assert!(guard.value().is_none());
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod multi {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let guard1 = locking.lock(&pool, 1);
assert!(guard1.value().is_none());
assert_eq!(1, pool.borrow().num_entries_or_locked());
let guard2 = locking.lock(&pool, 2);
assert!(guard2.value().is_none());
assert_eq!(2, pool.borrow().num_entries_or_locked());
let guard3 = locking.lock(&pool, 3);
assert!(guard3.value().is_none());
assert_eq!(3, pool.borrow().num_entries_or_locked());
std::mem::drop(guard2);
assert_eq!(2, pool.borrow().num_entries_or_locked());
std::mem::drop(guard1);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard3);
assert_eq!(0, pool.borrow().num_entries_or_locked());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let guard1 = locking.lock(&pool, 1).await;
assert!(guard1.value().is_none());
assert_eq!(1, pool.borrow().num_entries_or_locked());
let guard2 = locking.lock(&pool, 2).await;
assert!(guard2.value().is_none());
assert_eq!(2, pool.borrow().num_entries_or_locked());
let guard3 = locking.lock(&pool, 3).await;
assert!(guard3.value().is_none());
assert_eq!(3, pool.borrow().num_entries_or_locked());
std::mem::drop(guard2);
assert_eq!(2, pool.borrow().num_entries_or_locked());
std::mem::drop(guard1);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard3);
assert_eq!(0, pool.borrow().num_entries_or_locked());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod concurrent {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let guard = locking.lock(&*pool, 5);
let child = LockingThread::launch_thread_sync_lock(&locking, &pool, 5);
std::thread::sleep(Duration::from_millis(1000));
assert!(!child.entered_lock_section());
{
let _g = locking.lock(&*pool, 4);
}
std::mem::drop(guard);
child.wait_for_lock();
child.release_and_wait();
assert_eq!(0, (*pool).borrow().num_entries_or_locked());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let guard = locking.lock(&*pool, 5).await;
let child = LockingThread::launch_thread_async_lock(&locking, &pool, 5).await;
tokio::time::sleep(Duration::from_millis(1000)).await;
assert!(!child.entered_lock_section());
{
let _g = locking.lock(&*pool, 4).await;
}
std::mem::drop(guard);
child.wait_for_lock_async().await;
child.release_and_wait();
assert_eq!(0, (*pool).borrow().num_entries_or_locked());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod multi_concurrent {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let guard = locking.lock(&pool, 5);
let mut child1 = LockingThread::launch_thread_sync_lock(&locking, &pool, 5);
let mut child2 = LockingThread::launch_thread_sync_lock(&locking, &pool, 5);
thread::sleep(Duration::from_millis(1000));
assert!(!child1.entered_lock_section());
assert!(!child2.entered_lock_section());
{
let _g = locking.lock(&pool, 4);
}
std::mem::drop(guard);
$crate::tests::wait_for(|| {
child1.entered_lock_section() ^ child2.entered_lock_section()
}, Duration::from_secs(1));
child1.release();
child2.release();
child1.wait_for_lock();
child2.wait_for_lock();
child1.release_and_wait();
child2.release_and_wait();
assert_eq!(0, (*pool).borrow().num_entries_or_locked());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let guard = locking.lock(&pool, 5).await;
let mut child1 = LockingThread::launch_thread_async_lock(&locking, &pool, 5).await;
let mut child2 = LockingThread::launch_thread_async_lock(&locking, &pool, 5).await;
thread::sleep(Duration::from_millis(1000));
assert!(!child1.entered_lock_section());
assert!(!child2.entered_lock_section());
{
let _g = locking.lock(&pool, 4).await;
}
std::mem::drop(guard);
$crate::tests::wait_for_async(|| {
child1.entered_lock_section() ^ child2.entered_lock_section()
}, Duration::from_secs(1)).await;
child1.release();
child2.release();
child1.wait_for_lock();
child2.wait_for_lock();
child1.release_and_wait();
child2.release_and_wait();
assert_eq!(0, (*pool).borrow().num_entries_or_locked());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod try_lock {
use super::*;
#[test]
fn try_lock() {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.blocking_lock(5, SyncLimit::no_limit()).unwrap();
let result = pool.try_lock(5, SyncLimit::no_limit()).unwrap();
assert!(result.is_none());
{
let _g = pool.try_lock(4, SyncLimit::no_limit()).unwrap().unwrap();
}
std::mem::drop(guard);
{
let _g = pool.try_lock(5, SyncLimit::no_limit()).unwrap().unwrap();
}
assert_eq!(0, pool.num_entries_or_locked());
}
#[test]
fn try_lock_owned() {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.blocking_lock_owned(5, SyncLimit::no_limit()).unwrap();
let result = pool.try_lock_owned(5, SyncLimit::no_limit()).unwrap();
assert!(result.is_none());
{
let _g = pool.try_lock_owned(4, SyncLimit::no_limit()).unwrap().unwrap();
}
std::mem::drop(guard);
{
let _g = pool.try_lock_owned(5, SyncLimit::no_limit()).unwrap().unwrap();
}
assert_eq!(0, pool.num_entries_or_locked());
}
#[tokio::test]
async fn try_lock_async() {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.async_lock(5, AsyncLimit::no_limit()).await.unwrap();
let result = pool.try_lock_async(5, AsyncLimit::no_limit()).await.unwrap();
assert!(result.is_none());
{
let _g = pool.try_lock_async(4, AsyncLimit::no_limit()).await.unwrap().unwrap();
}
std::mem::drop(guard);
{
let _g = pool.try_lock_async(5, AsyncLimit::no_limit()).await.unwrap().unwrap();
}
assert_eq!(0, pool.num_entries_or_locked());
}
#[tokio::test]
async fn try_lock_owned_async() {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.async_lock_owned(5, AsyncLimit::no_limit()).await.unwrap();
let result = pool.try_lock_owned_async(5, AsyncLimit::no_limit()).await.unwrap();
assert!(result.is_none());
{
let _g = pool.try_lock_owned_async(4, AsyncLimit::no_limit()).await.unwrap().unwrap();
}
std::mem::drop(guard);
{
let _g = pool.try_lock_owned_async(5, AsyncLimit::no_limit()).await.unwrap().unwrap();
}
assert_eq!(0, pool.num_entries_or_locked());
}
}
mod guard {
use super::*;
mod insert {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
let prev_value = locking.lock(&pool, 4).insert(String::from("Previous value"));
assert_eq!(None, prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
let prev_value = guard.insert(String::from("Cache Entry Value"));
assert_eq!(Some(String::from("Previous value")), prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
let prev_value = locking.lock(&pool, 4).await.insert(String::from("Previous value"));
assert_eq!(None, prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
let prev_value = guard.insert(String::from("Cache Entry Value"));
assert_eq!(Some(String::from("Previous value")), prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
let prev_value = guard.insert(String::from("Cache Entry Value"));
assert_eq!(None, prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
let prev_value = guard.insert(String::from("Cache Entry Value"));
assert_eq!(None, prev_value);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod try_insert {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).insert(String::from("Previous Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
let result = guard.try_insert(String::from("Cache Entry Value"));
assert_eq!(Err(TryInsertError::AlreadyExists{value: String::from("Cache Entry Value")}), result);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Previous Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).await.insert(String::from("Previous Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
let result = guard.try_insert(String::from("Cache Entry Value"));
assert_eq!(Err(TryInsertError::AlreadyExists{value: String::from("Cache Entry Value")}), result);
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Previous Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
let new_entry = guard.try_insert(String::from("Cache Entry Value"));
assert_eq!(String::from("Cache Entry Value"), *new_entry.unwrap());
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
let new_entry = guard.try_insert(String::from("Cache Entry Value"));
assert_eq!(String::from("Cache Entry Value"), *new_entry.unwrap());
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry_and_modify_it_through_the_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
let new_entry = guard.try_insert(String::from("Cache Entry Value"));
*new_entry.unwrap() = String::from("New Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
let new_entry = guard.try_insert(String::from("Cache Entry Value"));
*new_entry.unwrap() = String::from("New Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod remove {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4)
.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
assert_eq!(Some("Cache Entry Value".into()), guard.remove());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).value(), None);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4)
.await
.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
assert_eq!(Some("Cache Entry Value".into()), guard.remove());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).await.value(), None);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry_in_nonempty_map {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4)
.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 5);
assert_eq!(None, guard.remove());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).value(), Some(&String::from("Cache Entry Value")));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4)
.await
.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 5).await;
assert_eq!(None, guard.remove());
std::mem::drop(guard);
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).await.value(), Some(&String::from("Cache Entry Value")));
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry_in_empty_map {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 5);
assert_eq!(None, guard.remove());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).value(), None);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 5).await;
assert_eq!(None, guard.remove());
std::mem::drop(guard);
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(locking.lock(&pool, 4).await.value(), None);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod value {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
std::mem::drop(guard);
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
std::mem::drop(guard);
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(
locking.lock(&pool, 4).value(),
None,
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(
locking.lock(&pool, 4).await.value(),
None,
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod value_mut {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).value_mut().unwrap());
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).await.value_mut().unwrap());
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod existing_entry_modify_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).value_mut().unwrap() = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).await.value_mut().unwrap() = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(None, locking.lock(&pool, 4).value_mut());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(None, locking.lock(&pool, 4).await.value_mut());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod value_or_insert {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).value_or_insert(String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).await.value_or_insert(String::from("Unused New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod existing_entry_modify_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).value_or_insert(String::from("Unused New Value")) = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).await.value_or_insert(String::from("Unused New Value")) = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(String::from("New Value"), *locking.lock(&pool, 4).value_or_insert(String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(String::from("New Value"), *locking.lock(&pool, 4).await.value_or_insert(String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry_modify_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
*locking.lock(&pool, 4).value_or_insert(String::from("New Value")) = String::from("Even Newer Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Even Newer Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
*locking.lock(&pool, 4).await.value_or_insert(String::from("New Value")) = String::from("Even Newer Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Even Newer Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod value_or_insert_with {
use super::*;
mod existing_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).value_or_insert_with(|| String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).await.value_or_insert_with(|| String::from("Unused New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod existing_entry_modify_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).value_or_insert_with(|| String::from("Unused New Value")) = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Cache Entry Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
*locking.lock(&pool, 4).await.value_or_insert_with(|| String::from("Unused New Value")) = String::from("New Cache Entry Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Cache Entry Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod existing_entry_then_callback_isnt_called {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).value_or_insert_with(|| panic!("Callback called")));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Cache Entry Value"));
assert_eq!(1, pool.borrow().num_entries_or_locked());
std::mem::drop(guard);
assert_eq!(String::from("Cache Entry Value"), *locking.lock(&pool, 4).await.value_or_insert_with(|| panic!("Callback called")));
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(String::from("New Value"), *locking.lock(&pool, 4).value_or_insert_with(|| String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("New Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
assert_eq!(String::from("New Value"), *locking.lock(&pool, 4).await.value_or_insert_with(|| String::from("New Value")));
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("New Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod nonexisting_entry_modify_returned_reference {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
*locking.lock(&pool, 4).value_or_insert_with(|| String::from("New Value")) = String::from("Even Newer Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).value(),
Some(&String::from("Even Newer Value"))
);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
*locking.lock(&pool, 4).await.value_or_insert_with(|| String::from("New Value")) = String::from("Even Newer Value");
assert_eq!(1, pool.borrow().num_entries_or_locked());
assert_eq!(
locking.lock(&pool, 4).await.value(),
Some(&String::from("Even Newer Value"))
);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod key {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
assert_eq!(4, *guard.key()); guard.insert(String::from("Cache Entry Value"));
std::mem::drop(guard);
assert_eq!(4, *locking.lock(&pool, 4).key()); }
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
assert_eq!(4, *guard.key()); guard.insert(String::from("Cache Entry Value"));
std::mem::drop(guard);
assert_eq!(4, *locking.lock(&pool, 4).await.key()); }
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod multiple_operations_on_same_guard {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
assert_eq!(None, guard.value());
assert_eq!(None, guard.insert(String::from("Cache Entry Value")));
assert_eq!(&String::from("Cache Entry Value"), guard.value().unwrap());
*guard.value_mut().unwrap() = String::from("Another value");
assert_eq!(&String::from("Another value"), guard.value().unwrap());
assert_eq!(Some(String::from("Another value")), guard.remove());
assert_eq!(None, guard.value());
assert_eq!(None, guard.insert(String::from("Last Value")));
std::mem::drop(guard);
assert_eq!(String::from("Last Value"), *locking.lock(&pool, 4).value().unwrap());
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(0, pool.borrow().num_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
assert_eq!(None, guard.value());
assert_eq!(None, guard.insert(String::from("Cache Entry Value")));
assert_eq!(&String::from("Cache Entry Value"), guard.value().unwrap());
*guard.value_mut().unwrap() = String::from("Another value");
assert_eq!(&String::from("Another value"), guard.value().unwrap());
assert_eq!(Some(String::from("Another value")), guard.remove());
assert_eq!(None, guard.value());
assert_eq!(None, guard.insert(String::from("Last Value")));
std::mem::drop(guard);
assert_eq!(String::from("Last Value"), *locking.lock(&pool, 4).await.value().unwrap());
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod keys_with_entries_or_locked {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
let guard = locking.lock(&pool, 4);
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.insert(String::from("Value"));
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
let mut guard = locking.lock(&pool, 4);
guard.remove();
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
locking.lock(&pool, 4).insert(String::from("Content"));
locking.lock(&pool, 5).insert(String::from("Content"));
locking.lock(&pool, 6).insert(String::from("Content"));
let mut keys = pool.borrow().keys_with_entries_or_locked();
keys.sort();
assert_eq!(vec![4, 5, 6], keys);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
let guard = locking.lock(&pool, 4).await;
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.insert(String::from("Value"));
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
let mut guard = locking.lock(&pool, 4).await;
guard.remove();
assert_eq!(vec![4], pool.borrow().keys_with_entries_or_locked());
std::mem::drop(guard);
assert_eq!(Vec::<isize>::new(), pool.borrow().keys_with_entries_or_locked());
locking.lock(&pool, 4).await.insert(String::from("Content"));
locking.lock(&pool, 5).await.insert(String::from("Content"));
locking.lock(&pool, 6).await.insert(String::from("Content"));
let mut keys = pool.borrow().keys_with_entries_or_locked();
keys.sort();
assert_eq!(vec![4, 5, 6], keys);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod into_entries_unordered {
use super::*;
mod map_with_0_entries {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
assert_eq!(Vec::<(isize, String)>::new(), iter);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
assert_eq!(Vec::<(isize, String)>::new(), iter);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_1_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).insert(String::from("Value"));
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
$crate::tests::assert_vec_eq_unordered(vec![(4, String::from("Value"))], iter);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).await.insert(String::from("Value"));
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
$crate::tests::assert_vec_eq_unordered(vec![(4, String::from("Value"))], iter);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_multiple_entries {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).insert(String::from("Value 4"));
locking.lock(&pool, 5).insert(String::from("Value 5"));
locking.lock(&pool, 3).insert(String::from("Value 3"));
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
$crate::tests::assert_vec_eq_unordered(vec![
(3, String::from("Value 3")),
(4, String::from("Value 4")),
(5, String::from("Value 5")),
], iter);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
locking.lock(&pool, 5).await.insert(String::from("Value 5"));
locking.lock(&pool, 3).await.insert(String::from("Value 3"));
let iter = locking.extract(pool).into_entries_unordered().collect::<Vec<(isize, String)>>();
$crate::tests::assert_vec_eq_unordered(vec![
(3, String::from("Value 3")),
(4, String::from("Value 4")),
(5, String::from("Value 5")),
], iter);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod lock_all_entries {
use super::*;
mod when_all_are_unlocked {
use super::*;
mod map_with_0_entries {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
let guards: Vec<(isize, Option<String>)> =
futures::executor::block_on(
futures::executor::block_on(pool.borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
);
$crate::tests::assert_vec_eq_unordered(Vec::<(isize, Option<String>)>::new(), guards);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
let guards: Vec<(isize, Option<String>)> =
pool.borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
.await;
$crate::tests::assert_vec_eq_unordered(Vec::<(isize, Option<String>)>::new(), guards);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_1_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).insert(String::from("Value"));
let guards: Vec<(isize, Option<String>)> =
futures::executor::block_on(
futures::executor::block_on(pool.borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
);
$crate::tests::assert_vec_eq_unordered(vec![(4, Some(String::from("Value")))], guards);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).await.insert(String::from("Value"));
let guards: Vec<(isize, Option<String>)> =
pool.borrow()
.lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
.await;
$crate::tests::assert_vec_eq_unordered(vec![(4, Some(String::from("Value")))], guards);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_multiple_entries {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>>,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).insert(String::from("Value 4"));
locking.lock(&pool, 5).insert(String::from("Value 5"));
locking.lock(&pool, 3).insert(String::from("Value 3"));
let guards: Vec<(isize, Option<String>)> =
futures::executor::block_on(
futures::executor::block_on(pool.borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
);
$crate::tests::assert_vec_eq_unordered(vec![
(3, Some(String::from("Value 3"))),
(4, Some(String::from("Value 4"))),
(5, Some(String::from("Value 5"))),
], guards);
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String>)
where
S: Borrow<$lockable_type<isize, String>> + Sync,
{
let pool = locking.new_lockable();
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
locking.lock(&pool, 5).await.insert(String::from("Value 5"));
locking.lock(&pool, 3).await.insert(String::from("Value 3"));
let guards: Vec<(isize, Option<String>)> =
pool.borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()))
.collect()
.await;
$crate::tests::assert_vec_eq_unordered(vec![
(3, Some(String::from("Value 3"))),
(4, Some(String::from("Value 4"))),
(5, Some(String::from("Value 5"))),
], guards);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod when_some_are_locked {
use super::*;
mod map_with_1_entry {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).insert(String::from("Value"));
let child = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).await.insert(String::from("Value"));
let child = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
child.wait_for_lock_async().await;
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_two_entries_all_are_locked {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).insert(String::from("Value 4"));
locking.lock(&pool, 5).insert(String::from("Value 5"));
let child1 = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
let child2 = LockingThread::launch_thread_sync_lock(&locking, &pool, 5);
child1.wait_for_lock();
child1.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child1.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child2.release_and_wait();
assert_eq!(Some((5, Some(String::from("Value 5")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
locking.lock(&pool, 5).await.insert(String::from("Value 5"));
let child1 = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
let child2 = LockingThread::launch_thread_async_lock(&locking, &pool, 5).await;
child1.wait_for_lock_async().await;
child1.wait_for_lock_async().await;
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child1.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child2.release_and_wait();
assert_eq!(Some((5, Some(String::from("Value 5")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod map_with_two_entries_some_are_locked {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 3).insert(String::from("Value 3"));
locking.lock(&pool, 4).insert(String::from("Value 4"));
locking.lock(&pool, 5).insert(String::from("Value 5"));
locking.lock(&pool, 6).insert(String::from("Value 6"));
let child1 = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
let child2 = LockingThread::launch_thread_sync_lock(&locking, &pool, 5);
child1.wait_for_lock();
child2.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
let values = vec![
futures::executor::block_on(guards_stream.next()),
futures::executor::block_on(guards_stream.next()),
];
$crate::tests::assert_vec_eq_unordered(
vec![
Some((3, Some(String::from("Value 3")))),
Some((6, Some(String::from("Value 6")))),
],
values,
);
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child1.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child2.release_and_wait();
assert_eq!(Some((5, Some(String::from("Value 5")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 3).await.insert(String::from("Value 3"));
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
locking.lock(&pool, 5).await.insert(String::from("Value 5"));
locking.lock(&pool, 6).await.insert(String::from("Value 6"));
let child1 = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
let child2 = LockingThread::launch_thread_async_lock(&locking, &pool, 5).await;
child1.wait_for_lock();
child2.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
let values = vec![
guards_stream.next().await,
guards_stream.next().await,
];
$crate::tests::assert_vec_eq_unordered(
vec![
Some((3, Some(String::from("Value 3")))),
Some((6, Some(String::from("Value 6")))),
],
values,
);
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child1.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child2.release_and_wait();
assert_eq!(Some((5, Some(String::from("Value 5")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod locked_entry_existence {
use super::*;
mod given_preexisting_when_not_deleted_while_locked_then_is_returned {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).insert(String::from("Value 4"));
let child = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
let child = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
child.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
tokio::time::sleep(Duration::from_millis(1000)).await;
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod given_preexisting_when_deleted_while_locked_then_is_not_returned {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).insert(String::from("Value 4"));
let child = LockingThread::launch_thread_sync_lock_with_callback(&locking, &pool, 4, |guard| {
guard.remove();
});
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
child.release_and_wait();
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
let child = LockingThread::launch_thread_async_lock_with_callback(&locking, &pool, 4, |guard| {
guard.remove();
}).await;
child.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
child.release_and_wait();
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod given_not_preexisting_when_not_created_while_locked_then_is_not_returned {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let child = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
child.release_and_wait();
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let child = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
child.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
child.release_and_wait();
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
mod given_not_preexisting_when_created_while_locked_then_is_returned {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let child = LockingThread::launch_thread_sync_lock_with_callback(&locking, &pool, 4, |guard| {
guard.insert(String::from("Value 4"));
});
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
let child = LockingThread::launch_thread_async_lock_with_callback(&locking, &pool, 4, |guard| {
guard.insert(String::from("Value 4"));
}).await;
child.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
tokio::time::sleep(Duration::from_millis(1000)).await;
assert_eq!(None, guards_stream.next_if_ready());
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
mod running_stream_doesnt_block_whole_map {
use super::*;
fn test_sync<S>(locking: impl SyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).insert(String::from("Value 4"));
let child = LockingThread::launch_thread_sync_lock(&locking, &pool, 4);
child.wait_for_lock();
let mut guards_stream =
futures::executor::block_on(pool.deref().borrow().lock_all_entries())
.map(|guard| (*guard.key(), guard.value().cloned()));
thread::sleep(Duration::from_millis(1000));
assert_eq!(None, guards_stream.next_if_ready());
let other_guard_1 = locking.lock(&pool, 5);
let _other_guard_2 = locking.lock(&pool, 6);
std::mem::drop(other_guard_1);
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, futures::executor::block_on(guards_stream.next()));
}
async fn test_async<S>(locking: impl AsyncLocking<S, isize, String> + Sync + 'static)
where
S: Borrow<$lockable_type<isize, String>> + Send + Sync + 'static,
{
let pool = Arc::new(locking.new_lockable());
locking.lock(&pool, 4).await.insert(String::from("Value 4"));
let child = LockingThread::launch_thread_async_lock(&locking, &pool, 4).await;
child.wait_for_lock();
let mut guards_stream =
pool.deref().borrow().lock_all_entries()
.await
.map(|guard| (*guard.key(), guard.value().cloned()));
tokio::time::sleep(Duration::from_millis(1000)).await;
assert_eq!(None, guards_stream.next_if_ready());
let other_guard_1 = locking.lock(&pool, 5).await;
let _other_guard_2 = locking.lock(&pool, 6).await;
std::mem::drop(other_guard_1);
child.release_and_wait();
assert_eq!(Some((4, Some(String::from("Value 4")))), guards_stream.next_if_ready());
assert_eq!(None, guards_stream.next().await);
}
$crate::instantiate_lockable_tests!(@gen_tests, $lockable_type, test_sync, test_async);
}
}
#[test]
fn blocking_lock_owned_guards_can_be_passed_around() {
let make_guard = || {
let pool = Arc::new($lockable_type::<isize, String>::new());
pool.blocking_lock_owned(5, SyncLimit::no_limit()).unwrap()
};
let _guard = make_guard();
}
#[tokio::test]
async fn async_lock_owned_guards_can_be_passed_around() {
let make_guard = async || {
let pool = Arc::new($lockable_type::<isize, String>::new());
pool.async_lock_owned(5, AsyncLimit::no_limit()).await.unwrap()
};
let _guard = make_guard().await;
}
#[test]
fn test_try_lock_owned_guards_can_be_passed_around() {
let make_guard = || {
let pool = Arc::new($lockable_type::<isize, String>::new());
pool.try_lock_owned(5, SyncLimit::no_limit()).unwrap()
};
let guard = make_guard();
assert!(guard.is_some());
}
fn assert_is_send(_v: impl Send) {}
#[tokio::test]
async fn async_lock_guards_can_be_held_across_await_points() {
let task = async {
let pool = $lockable_type::<isize, String>::new();
let guard = pool.async_lock(3, AsyncLimit::no_limit()).await.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
std::mem::drop(guard);
};
assert_is_send(task);
}
#[tokio::test]
async fn async_lock_owned_guards_can_be_held_across_await_points() {
let task = async {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.async_lock_owned(3, AsyncLimit::no_limit()).await.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
std::mem::drop(guard);
};
assert_is_send(task);
}
#[tokio::test]
async fn try_lock_async_guards_can_be_held_across_await_points() {
let task = async {
let pool = $lockable_type::<isize, String>::new();
let guard = pool.try_lock_async(3, AsyncLimit::no_limit()).await.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
std::mem::drop(guard);
};
assert_is_send(task);
}
#[tokio::test]
async fn try_lock_owned_async_guards_can_be_held_across_await_points() {
let task = async {
let pool = Arc::new($lockable_type::<isize, String>::new());
let guard = pool.try_lock_owned_async(3, AsyncLimit::no_limit()).await.unwrap();
tokio::time::sleep(Duration::from_millis(10)).await;
std::mem::drop(guard);
};
assert_is_send(task);
}
}
}