/
serial_code_lock.rs
122 lines (105 loc) · 3.4 KB
/
serial_code_lock.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#![allow(clippy::await_holding_lock)]
use crate::code_lock::{check_new_key, LOCKS};
use std::time::Duration;
#[doc(hidden)]
pub fn local_serial_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
function: fn() -> Result<(), E>,
) -> Result<(), E> {
check_new_key(name, max_wait);
let unlock = LOCKS.get(name).expect("key to be set");
// _guard needs to be named to avoid being instant dropped
let _guard = unlock.lock();
function()
}
#[doc(hidden)]
pub fn local_serial_core(name: &str, max_wait: Option<Duration>, function: fn()) {
check_new_key(name, max_wait);
let unlock = LOCKS.get(name).expect("key to be set");
// _guard needs to be named to avoid being instant dropped
let _guard = unlock.lock();
function();
}
#[doc(hidden)]
#[cfg(feature = "async")]
pub async fn local_async_serial_core_with_return<E>(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = Result<(), E>>,
) -> Result<(), E> {
check_new_key(name, max_wait);
let unlock = LOCKS.get(name).expect("key to be set");
// _guard needs to be named to avoid being instant dropped
let _guard = unlock.lock();
fut.await
}
#[doc(hidden)]
#[cfg(feature = "async")]
pub async fn local_async_serial_core(
name: &str,
max_wait: Option<Duration>,
fut: impl std::future::Future<Output = ()>,
) {
check_new_key(name, max_wait);
let unlock = LOCKS.get(name).expect("key to be set");
// _guard needs to be named to avoid being instant dropped
let _guard = unlock.lock();
fut.await;
}
#[cfg(test)]
#[allow(clippy::print_stdout)]
mod tests {
use super::local_serial_core;
use crate::code_lock::{check_new_key, LOCKS};
use itertools::Itertools;
use parking_lot::RwLock;
use std::{
sync::{Arc, Barrier},
thread,
time::Duration,
};
#[test]
fn test_hammer_check_new_key() {
let ptrs = Arc::new(RwLock::new(Vec::new()));
let mut threads = Vec::new();
let count = 100;
let barrier = Arc::new(Barrier::new(count));
for _ in 0..count {
let local_locks = LOCKS.clone();
let local_ptrs = ptrs.clone();
let c = barrier.clone();
threads.push(thread::spawn(move || {
c.wait();
check_new_key("foo", None);
{
let unlock = local_locks.get("foo").expect("read didn't work");
let mutex = unlock.value();
let mut ptr_guard = local_ptrs
.try_write_for(Duration::from_secs(1))
.expect("write lock didn't work");
ptr_guard.push(mutex.id);
}
c.wait();
}));
}
for thread in threads {
thread.join().expect("thread join worked");
}
let ptrs_read_lock = ptrs
.try_read_recursive_for(Duration::from_secs(1))
.expect("ptrs read work");
assert_eq!(ptrs_read_lock.len(), count);
println!("{:?}", ptrs_read_lock);
assert_eq!(ptrs_read_lock.iter().unique().count(), 1);
}
#[test]
fn unlock_on_assert() {
let _ = std::panic::catch_unwind(|| {
local_serial_core("assert", None, || {
assert!(false);
})
});
assert!(!LOCKS.get("assert").unwrap().is_locked());
}
}