| Crates.io | lockerroom |
| lib.rs | lockerroom |
| version | 0.2.1 |
| created_at | 2024-07-16 16:23:04.877849+00 |
| updated_at | 2024-07-25 18:02:59.760426+00 |
| 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.
}
}