Crates.io | lockerroom |
lib.rs | lockerroom |
version | 0.2.1 |
source | src |
created_at | 2024-07-16 16:23:04.877849 |
updated_at | 2024-07-25 18:02:59.760426 |
description | Readers-writer access to individual cells of your collection! |
homepage | |
repository | https://github.com/nutsalhan87/lockerroom |
max_upload_size | |
id | 1305244 |
size | 51,351 |
The central features of the crate is implemented by these structures. More specifically, they provide such functionality:
LockerRoom::read_cell
and LockerRoomAsync::read_cell
;LockerRoom::write_cell
and LockerRoomAsync::write_cell
;LockerRoom::lock_room
and LockerRoomAsync::lock_room
.But LockerRoomAsync
is optional - you need to enable feature async
to use it. It depends on
tokio
's RwLock
.
let v = vec![0, 1, 2, 3, 4, 5];
let locker_room: LockerRoom<_> = v.into();
let locker_room = Arc::new(locker_room);
thread::scope(|scope| {
scope.spawn(|| *locker_room.write_cell(0).unwrap() += 1);
scope.spawn(|| *locker_room.write_cell(0).unwrap() += 2);
});
assert_eq!(3, *locker_room.read_cell(0).unwrap());
let v = vec![0, 1, 2, 3, 4, 5];
let locker_room: LockerRoomAsync<_> = v.into();
let locker_room = Arc::new(locker_room);
let locker_room_cloned = Arc::clone(&locker_room);
let join1 = spawn(async move { *locker_room_cloned.write_cell(0).await.unwrap() += 1 });
let locker_room_cloned = Arc::clone(&locker_room);
let join2 = spawn(async move { *locker_room_cloned.write_cell(0).await.unwrap() += 2 });
join!(join1, join2);
assert_eq!(3, *locker_room.read_cell(0).await.unwrap());
Carefully block multiple cells in one scope. Otherwise, situation like this may occur:
// Thread 1 | // Thread 2
let _w1 = locker_room.write_cell(0); |
| let _w1 = locker_room.write_cell(1);
// will block
let _w2 = locker_room.write_cell(1); |
| // will deadlock
| let _w2 = locker_room.write_cell(0);
By default you can create LockerRoom
and LockerRoomAsync
from array
, Vec
, VecDeque
, HashMap
and BTreeMap
.
But the crate provides traits, by which implementing to your collection, you can make it compatible with LockerRoom
and LockerRoomAsync
.
Crucial part of the crate that helps your collection to be compatible with LockerRoom
and LockerRoomAsync
.
Just implement it into your collection and everything will work!
Let's implement the trait for the struct from Index
's example:
enum Nucleotide {
C,
A,
G,
T,
}
struct NucleotideCount {
pub a: usize,
pub c: usize,
pub g: usize,
pub t: usize,
}
impl Collection for NucleotideCount {
type Output = usize;
type Idx = Nucleotide;
type ShadowLocks = NucleotideShadowLocks;
fn index(&self, index: impl Borrow<Self::Idx>) -> Option<&Self::Output> {
Some(match index.borrow() {
Nucleotide::A => &self.a,
Nucleotide::C => &self.c,
Nucleotide::G => &self.g,
Nucleotide::T => &self.t,
})
}
fn index_mut(&mut self, index: impl Borrow<Self::Idx>) -> Option<&mut Self::Output> {
Some(match index.borrow() {
Nucleotide::A => &mut self.a,
Nucleotide::C => &mut self.c,
Nucleotide::G => &mut self.g,
Nucleotide::T => &mut self.t,
})
}
fn indices(&self) -> impl Iterator<Item = Self::Idx> {
[Nucleotide::A, Nucleotide::C, Nucleotide::G, Nucleotide::T].into_iter()
}
fn shadow_locks(&self) -> Self::ShadowLocks {
Default::default()
}
}
struct NucleotideShadowLocks {
a: RwLock<()>,
c: RwLock<()>,
g: RwLock<()>,
t: RwLock<()>,
}
impl ShadowLocksCollection for NucleotideShadowLocks {
type Idx = Nucleotide;
fn index(&self, index: impl Borrow<Self::Idx>) -> Option<&RwLock<()>> {
Some(match index.borrow() {
Nucleotide::A => &self.a,
Nucleotide::C => &self.c,
Nucleotide::G => &self.g,
Nucleotide::T => &self.t,
})
}
fn update_indices(&mut self, _indices: impl Iterator<Item = Self::Idx>) {
// No need to reindex because NucleotideShadowLocks has static structure.
}
}
If feature async
is enabled, Collection
must also include Collection::ShadowLocksAsync
type and Collection::shadow_locks_async
method.
Thus ShadowLocksCollectionAsync
must be implemented.
impl Collection for NucleotideCount {
// ...
type ShadowLocksAsync = NucleotideShadowLocksAsync;
// ...
fn shadow_locks_async(&self) -> Self::ShadowLocksAsync {
Default::default()
}
}
struct NucleotideShadowLocksAsync {
a: tokio::sync::RwLock<()>,
c: tokio::sync::RwLock<()>,
g: tokio::sync::RwLock<()>,
t: tokio::sync::RwLock<()>,
}
impl ShadowLocksCollectionAsync for NucleotideShadowLocksAsync {
type Idx = Nucleotide;
fn index(&self, index: impl Borrow<Self::Idx>) -> Option<&tokio::sync::RwLock<()>> {
Some(match index.borrow() {
Nucleotide::A => &self.a,
Nucleotide::C => &self.c,
Nucleotide::G => &self.g,
Nucleotide::T => &self.t,
})
}
fn update_indices(&mut self, _indices: impl Iterator<Item = Self::Idx>) {
// No need to reindex because NucleotideShadowLocksAsync has static structure.
}
}