Skip to content

Commit 301f598

Browse files
committed
Hooks
1 parent 3e539c6 commit 301f598

File tree

6 files changed

+148
-63
lines changed

6 files changed

+148
-63
lines changed

Cargo.toml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "slock"
3-
version = "0.1.1"
3+
version = "0.1.2"
44
authors = ["Brandon Dyer <[email protected]>"]
55
edition = "2018"
66
description = "An async mutex that never deadlocks."
@@ -12,8 +12,17 @@ categories = ["asynchronous", "concurrency", "memory-management", "rust-patterns
1212

1313
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1414

15-
[dependencies]
16-
futures = "0.3"
15+
[features]
16+
default = ["futures", "blocking"]
17+
blocking = ["futures"]
18+
19+
[dependencies.futures]
20+
version = "0.3"
21+
optional = true
22+
23+
[dependencies.async-std]
24+
version = "1.6"
1725

1826
[dev-dependencies]
1927
lazy_static = "1.4"
28+
futures = "0.3"

src/blocking.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![cfg(feature = "blocking")]
2+
3+
use crate::*;
4+
use futures::executor::block_on;
5+
6+
macro_rules! impl_op {
7+
($name:ident, $name_assign:ident, $fun:ident, $op:tt) => {
8+
impl<T> std::ops::$name_assign<T> for Slock<T>
9+
where
10+
T: Copy + std::ops::$name<T, Output = T>,
11+
T: From<T>,
12+
{
13+
fn $fun(&mut self, other: T) {
14+
block_on(self.set(|v| v $op other));
15+
}
16+
}
17+
};
18+
}
19+
20+
impl_op!(Add, AddAssign, add_assign, +);
21+
impl_op!(Sub, SubAssign, sub_assign, -);
22+
impl_op!(Mul, MulAssign, mul_assign, *);
23+
impl_op!(Div, DivAssign, div_assign, /);
24+
impl_op!(Rem, RemAssign, rem_assign, %);
25+
26+
impl<T: Clone> Clone for Slock<T> {
27+
/// Creates a clone of the lock and its data.
28+
/// This operation is blocking.
29+
/// Prefer `clone_async`
30+
fn clone(&self) -> Self {
31+
return Slock::new(block_on(self.get_clone()));
32+
}
33+
}

src/lib.rs

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,34 @@
5858
//! # };
5959
//! ```
6060
61-
use futures::executor::block_on;
61+
pub use async_std::future::{timeout, TimeoutError};
6262
use std::{
6363
cmp::Eq,
6464
collections::HashMap,
6565
hash::Hash,
6666
sync::{Arc, RwLock},
6767
};
6868

69+
pub struct SlockData<T> {
70+
pub version: u32,
71+
pub value: T,
72+
pub hook: Option<Box<dyn FnMut(&T)>>,
73+
}
74+
6975
pub struct Slock<T> {
70-
lock: Arc<RwLock<T>>,
76+
lock: Arc<RwLock<SlockData<T>>>,
7177
}
7278

7379
impl<T> Slock<T> {
7480
/// Create a new Slock with a given initial value.
7581
pub fn new(value: T) -> Self {
82+
let data = SlockData {
83+
version: 0,
84+
value,
85+
hook: None,
86+
};
7687
Self {
77-
lock: Arc::new(RwLock::new(value)),
88+
lock: Arc::new(RwLock::new(data)),
7889
}
7990
}
8091

@@ -87,12 +98,17 @@ impl<T> Slock<T> {
8798
/// let name = lock.map(|v| v.name).await;
8899
/// # };
89100
/// ```
90-
pub async fn map<F, U>(&self, mapper: F) -> U
101+
pub async fn map<F, U>(&self, mapper: F) -> Result<U, TimeoutError>
91102
where
92103
F: FnOnce(&T) -> U,
93104
{
94105
match self.lock.read() {
95-
Ok(v) => mapper(&*v),
106+
Ok(v) => {
107+
timeout(std::time::Duration::from_secs(1), async {
108+
mapper(&v.value)
109+
})
110+
.await
111+
}
96112
Err(_) => panic!("Slock could not read for map!"),
97113
}
98114
}
@@ -111,12 +127,23 @@ impl<T> Slock<T> {
111127
F: FnOnce(T) -> T,
112128
{
113129
match self.lock.write() {
114-
Ok(mut v) => {
115-
let ptr = &mut *v as *mut T;
130+
Ok(mut data) => {
131+
let ptr = &mut data.value as *mut T;
116132
unsafe {
117-
let new = setter(ptr.read());
118-
ptr.write(new);
133+
let new = timeout(std::time::Duration::from_secs(1), async {
134+
setter(ptr.read())
135+
})
136+
.await;
137+
if let Ok(new) = new {
138+
timeout(std::time::Duration::from_secs(1), async {
139+
data.hook.as_mut().map(|hook| hook(&new));
140+
})
141+
.await
142+
.ok();
143+
ptr.write(new);
144+
}
119145
}
146+
data.version += 1;
120147
}
121148
Err(_) => panic!("Slock could not write!"),
122149
}
@@ -139,16 +166,28 @@ impl<T> Slock<T> {
139166
/// Returns the lock's atomic reference counter.
140167
/// This is unsafe as using it can no longer guarantee
141168
/// deadlocks won't occur.
142-
pub unsafe fn get_raw_arc(&self) -> Arc<RwLock<T>> {
169+
pub unsafe fn get_raw_arc(&self) -> Arc<RwLock<SlockData<T>>> {
143170
self.lock.clone()
144171
}
172+
173+
pub fn hook<F: 'static>(&self, hook: F)
174+
where
175+
F: FnMut(&T),
176+
{
177+
match self.lock.write() {
178+
Ok(mut data) => {
179+
data.hook = Some(Box::new(hook));
180+
}
181+
Err(_) => panic!("Slock could not write!"),
182+
}
183+
}
145184
}
146185

147186
impl<T: Clone> Slock<T> {
148187
/// Returns a clone of the lock's data.
149188
pub async fn get_clone(&self) -> T {
150189
match self.lock.read() {
151-
Ok(v) => v.clone(),
190+
Ok(v) => v.value.clone(),
152191
Err(_) => panic!("Slock could not read for clone!"),
153192
}
154193
}
@@ -174,7 +213,7 @@ impl<T> Slock<Vec<T>> {
174213
impl<T> Slock<Slock<T>> {
175214
/// Converts from `Slock<Slock<T>>` to `Slock<T>`
176215
pub async fn flatten(&self) -> Slock<T> {
177-
self.map(|inner| inner.split()).await
216+
self.map(|inner| inner.split()).await.unwrap()
178217
}
179218
}
180219

@@ -210,44 +249,21 @@ impl<K: Eq + Hash + Copy, V> SlockMap<K, V> {
210249
hash_map.get(&key).map(|inner| inner.split())
211250
})
212251
.await
252+
.unwrap()
213253
}
214254
}
215255

216-
macro_rules! impl_op {
217-
($name:ident, $name_assign:ident, $fun:ident, $op:tt) => {
218-
impl<T> std::ops::$name_assign<T> for Slock<T>
219-
where
220-
T: Copy + std::ops::$name<T, Output = T>,
221-
T: From<T>,
222-
{
223-
fn $fun(&mut self, other: T) {
224-
block_on(self.set(|v| v $op other));
225-
}
226-
}
227-
};
228-
}
229-
230-
impl_op!(Add, AddAssign, add_assign, +);
231-
impl_op!(Sub, SubAssign, sub_assign, -);
232-
impl_op!(Mul, MulAssign, mul_assign, *);
233-
impl_op!(Div, DivAssign, div_assign, /);
234-
impl_op!(Rem, RemAssign, rem_assign, %);
235-
236256
impl<T: Copy> Slock<T> {
237257
/// If a lock's data implements copy, this will return an owned copy of it.
238258
pub async fn get(&self) -> T {
239259
match self.lock.read() {
240-
Ok(v) => *v,
260+
Ok(v) => v.value,
241261
Err(_) => panic!("Slock could not read for clone!"),
242262
}
243263
}
244264
}
245265

246-
impl<T: Clone> Clone for Slock<T> {
247-
/// Creates a clone of the lock and its data.
248-
/// This operation is blocking.
249-
/// Prefer `clone_async`
250-
fn clone(&self) -> Self {
251-
return Slock::new(block_on(self.get_clone()));
252-
}
253-
}
266+
pub mod blocking;
267+
268+
unsafe impl<T> Send for Slock<T> {}
269+
unsafe impl<T> Sync for Slock<T> {}

tests/blocking.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![cfg(feature = "blocking")]
2+
3+
use futures::executor::block_on;
4+
use slock::*;
5+
6+
#[test]
7+
fn arrithmetic() {
8+
let mut lock = Slock::new(0i32);
9+
lock += 1;
10+
assert_eq!(block_on(lock.get()), 1);
11+
lock -= 1;
12+
assert_eq!(block_on(lock.get()), 0);
13+
lock += 1;
14+
lock *= 2;
15+
assert_eq!(block_on(lock.get()), 2);
16+
lock /= 2;
17+
assert_eq!(block_on(lock.get()), 1);
18+
lock %= 1;
19+
assert_eq!(block_on(lock.get()), 0);
20+
}
21+
22+
#[test]
23+
fn cloning() {
24+
let lock = Slock::new(String::from("hello"));
25+
let lock_cloned = lock.clone();
26+
block_on(lock.set(|_| String::from("goodbye")));
27+
assert_eq!(block_on(lock_cloned.get_clone()), "hello");
28+
}

tests/hooks.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use futures::executor::block_on;
2+
use slock::*;
3+
4+
#[test]
5+
fn basic_hooks() {
6+
unsafe {
7+
let lock = Slock::new(());
8+
static mut COUNT: i32 = 0;
9+
lock.hook(|_| COUNT += 1);
10+
block_on(lock.set(|_| ()));
11+
block_on(lock.set(|_| ()));
12+
block_on(lock.set(|_| ()));
13+
assert_eq!(COUNT, 3);
14+
}
15+
}

tests/tests.rs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ fn mapping() {
5050
age: 32,
5151
});
5252

53-
let name = block_on(lock.map(|v| v.name));
54-
let age = block_on(lock.map(|v| v.age));
53+
let name = block_on(lock.map(|v| v.name)).unwrap();
54+
let age = block_on(lock.map(|v| v.age)).unwrap();
5555

5656
assert_eq!(name, "Bob");
5757
assert_eq!(age, 32);
@@ -63,7 +63,7 @@ fn vector() {
6363
let vec: Vec<i32> = Vec::new();
6464
let lock = Slock::new(vec);
6565
block_on(lock.push(1));
66-
assert_eq!(block_on(lock.map(|v| v[0])), 1);
66+
assert_eq!(block_on(lock.map(|v| v[0])).unwrap(), 1);
6767
}
6868

6969
/// Old value should Drop when a new value is created.
@@ -109,19 +109,3 @@ fn non_destruction() {
109109
std::mem::drop(lock);
110110
assert_eq!(COUNT.load(Ordering::SeqCst), 1);
111111
}
112-
113-
#[test]
114-
fn arrithmetic() {
115-
let mut lock = Slock::new(0i32);
116-
lock += 1;
117-
assert_eq!(block_on(lock.get()), 1);
118-
lock -= 1;
119-
assert_eq!(block_on(lock.get()), 0);
120-
lock += 1;
121-
lock *= 2;
122-
assert_eq!(block_on(lock.get()), 2);
123-
lock /= 2;
124-
assert_eq!(block_on(lock.get()), 1);
125-
lock %= 1;
126-
assert_eq!(block_on(lock.get()), 0);
127-
}

0 commit comments

Comments
 (0)