Skip to content

Commit

Permalink
Upgraded to tokio. Fixed several issues
Browse files Browse the repository at this point in the history
  • Loading branch information
BrandonDyer64 committed Nov 27, 2022
1 parent 301f598 commit 719df35
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 138 deletions.
10 changes: 1 addition & 9 deletions .gitignore
@@ -1,18 +1,10 @@
# Generated by Cargo
# will have compiled files and executables
/target/
/target

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk


#Added by cargo
#
#already existing elements were commented out

/target
#Cargo.lock
27 changes: 8 additions & 19 deletions Cargo.toml
@@ -1,28 +1,17 @@
[package]
name = "slock"
version = "0.1.2"
authors = ["Brandon Dyer <BrandonDyer64@gmail.com>"]
edition = "2018"
categories = ["asynchronous", "concurrency", "memory-management", "rust-patterns"]
description = "An async mutex that never deadlocks."
edition = "2021"
keywords = ["mutex", "smart", "lock", "async"]
license = "MIT"
name = "slock"
readme = "README.md"
repository = "https://github.com/BrokenLamp/slock-rs"
license = "MIT"
keywords = ["mutex", "smart", "lock", "async"]
categories = ["asynchronous", "concurrency", "memory-management", "rust-patterns"]

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

[features]
default = ["futures", "blocking"]
blocking = ["futures"]

[dependencies.futures]
version = "0.3"
optional = true
version = "0.2.0"

[dependencies.async-std]
version = "1.6"
[dependencies]
tokio = {version = "1.22", features = ["sync", "time", "rt", "macros"]}

[dev-dependencies]
lazy_static = "1.4"
futures = "0.3"
18 changes: 8 additions & 10 deletions README.md
Expand Up @@ -14,9 +14,9 @@ let lock = Slock::new(5i32);
// Change the lock's value
lock.set(|v| v + 1).await;

// Get the lock's value
// Get the lock's value if Copy
let value = lock.get().await;
// Or if the value doesn't implement copy
// Get the lock's value if Clone
let value = lock.get_clone().await;

assert_eq!(value, 6);
Expand All @@ -25,24 +25,22 @@ assert_eq!(value, 6);
It's also possible to extract only the data you need from larger structures without the need to clone the entire thing.

```rust
// A user struct that doesn't implement copy
struct User {
name: String,
age: i32,
// ... lots of other things
}

let user = Slock::new(User {
name: "Bob",
age: 32,
// ... lots of other things
});

// Get just the name
// This performs a clone on only the name
// Extract something that is Copy
let age = user.map(|v| v.age).await;

// Extract something that is Clone
let name = user.map(|v| v.name.clone()).await;

// Get just the age
// Extracts only the age, leaving everything else untouched
let age = user.map(|v| v.age).await;
// Increment `age` by 1
user.set(|v| v.age += 1).await;
```
110 changes: 51 additions & 59 deletions src/lib.rs
Expand Up @@ -20,7 +20,7 @@
//! println!("{}", value); // 6
//!
//! // Use in multiple threads
//! futures::join!(
//! tokio::join!(
//! do_something_in_a_thread(lock.split()),
//! do_something_else_in_a_thread(lock.split()),
//! do_another_thing_in_a_thread(lock.split()),
Expand All @@ -33,7 +33,7 @@
//! ### Don't access a Slock from within another
//!
//! Bad:
//! ```rust
//! ```rust,ignore
//! # use slock::*;
//! # use futures::executor::block_on;
//! # async {
Expand All @@ -58,12 +58,11 @@
//! # };
//! ```

pub use async_std::future::{timeout, TimeoutError};
use std::{
cmp::Eq,
collections::HashMap,
hash::Hash,
sync::{Arc, RwLock},
use std::{cmp::Eq, collections::HashMap, hash::Hash, sync::Arc};

use tokio::{
sync::RwLock,
time::{error::Elapsed, timeout},
};

pub struct SlockData<T> {
Expand Down Expand Up @@ -98,25 +97,22 @@ impl<T> Slock<T> {
/// let name = lock.map(|v| v.name).await;
/// # };
/// ```
pub async fn map<F, U>(&self, mapper: F) -> Result<U, TimeoutError>
pub async fn map<F, U>(&self, mapper: F) -> Result<U, Elapsed>
where
F: FnOnce(&T) -> U,
{
match self.lock.read() {
Ok(v) => {
timeout(std::time::Duration::from_secs(1), async {
mapper(&v.value)
})
.await
}
Err(_) => panic!("Slock could not read for map!"),
}
let v = self.lock.read().await;
timeout(std::time::Duration::from_secs(1), async {
mapper(&v.value)
})
.await
}

/// A setter for changing the internal data of the lock.
/// ```rust
/// # use slock::*;
/// # let lock = Slock::new(1i32);
/// let lock = Slock::new(1i32);
///
/// # async {
/// lock.set(|v| v + 1).await;
/// lock.set(|_| 6).await;
Expand All @@ -126,27 +122,22 @@ impl<T> Slock<T> {
where
F: FnOnce(T) -> T,
{
match self.lock.write() {
Ok(mut data) => {
let ptr = &mut data.value as *mut T;
unsafe {
let new = timeout(std::time::Duration::from_secs(1), async {
setter(ptr.read())
})
.await;
if let Ok(new) = new {
timeout(std::time::Duration::from_secs(1), async {
data.hook.as_mut().map(|hook| hook(&new));
})
.await
.ok();
ptr.write(new);
}
}
data.version += 1;
}
Err(_) => panic!("Slock could not write!"),
let mut data = self.lock.write().await;
let ptr = &mut data.value as *mut T;
let new = timeout(std::time::Duration::from_secs(1), async {
setter(unsafe { ptr.read() })
})
.await;
if let Ok(new) = new {
timeout(std::time::Duration::from_secs(1), async {
data.hook.as_mut().map(|hook| hook(&new));
})
.await
.ok();
unsafe { ptr.write(new) };
}

data.version += 1;
}

/// Create's a new lock pointing to the same data.
Expand All @@ -157,6 +148,7 @@ impl<T> Slock<T> {
/// let lock = Slock::new(0i32);
/// let the_same_lock = lock.split();
/// ```
#[deprecated = "Use `clone()` instead"]
pub fn split(&self) -> Self {
Self {
lock: self.lock.clone(),
Expand All @@ -170,30 +162,32 @@ impl<T> Slock<T> {
self.lock.clone()
}

pub fn hook<F: 'static>(&self, hook: F)
pub async fn hook<F: 'static>(&self, hook: F)
where
F: FnMut(&T),
{
match self.lock.write() {
Ok(mut data) => {
data.hook = Some(Box::new(hook));
}
Err(_) => panic!("Slock could not write!"),
let mut data = self.lock.write().await;
data.hook = Some(Box::new(hook));
}
}

impl<T> Clone for Slock<T> {
fn clone(&self) -> Self {
Self {
lock: self.lock.clone(),
}
}
}

impl<T: Clone> Slock<T> {
/// Returns a clone of the lock's data.
pub async fn get_clone(&self) -> T {
match self.lock.read() {
Ok(v) => v.value.clone(),
Err(_) => panic!("Slock could not read for clone!"),
}
let data = self.lock.read().await;
data.value.clone()
}

/// Creates a clone of the lock and its data.
pub async fn clone_async(&self) -> Self {
/// Create a new lock with data clone from this one.
pub async fn clone_deep(&self) -> Self {
return Slock::new(self.get_clone().await);
}
}
Expand All @@ -213,7 +207,7 @@ impl<T> Slock<Vec<T>> {
impl<T> Slock<Slock<T>> {
/// Converts from `Slock<Slock<T>>` to `Slock<T>`
pub async fn flatten(&self) -> Slock<T> {
self.map(|inner| inner.split()).await.unwrap()
self.map(|inner| inner.clone()).await.unwrap()
}
}

Expand Down Expand Up @@ -246,7 +240,7 @@ impl<K: Eq + Hash + Copy, V> SlockMap<K, V> {
pub async fn from_key(&self, key: K) -> Option<Slock<V>> {
self.map(|hash_map| {
let key = key;
hash_map.get(&key).map(|inner| inner.split())
hash_map.get(&key).map(|inner| inner.clone())
})
.await
.unwrap()
Expand All @@ -256,14 +250,12 @@ impl<K: Eq + Hash + Copy, V> SlockMap<K, V> {
impl<T: Copy> Slock<T> {
/// If a lock's data implements copy, this will return an owned copy of it.
pub async fn get(&self) -> T {
match self.lock.read() {
Ok(v) => v.value,
Err(_) => panic!("Slock could not read for clone!"),
}
let data = self.lock.read().await;
data.value
}
}

pub mod blocking;

unsafe impl<T> Send for Slock<T> {}
unsafe impl<T> Sync for Slock<T> {}
unsafe impl<T: Send> Send for Slock<T> {}
unsafe impl<T: Send> Sync for Slock<T> {}
14 changes: 7 additions & 7 deletions tests/hooks.rs
@@ -1,15 +1,15 @@
use futures::executor::block_on;
use slock::*;

#[test]
fn basic_hooks() {
#[tokio::test]
async fn basic_hooks() {
// SAFETY: Required to increment the static counter
unsafe {
let lock = Slock::new(());
static mut COUNT: i32 = 0;
lock.hook(|_| COUNT += 1);
block_on(lock.set(|_| ()));
block_on(lock.set(|_| ()));
block_on(lock.set(|_| ()));
lock.hook(|_| COUNT += 1).await;
lock.set(|_| ()).await;
lock.set(|_| ()).await;
lock.set(|_| ()).await;
assert_eq!(COUNT, 3);
}
}

0 comments on commit 719df35

Please sign in to comment.