🌎🔒 interlock

docs.rs

Experimental, requires unstable compiler features

Readers-writer locks designed for locking intervals. #![no_std] compatible.

```rust use std::pin::Pin; use interlock::{hl::slice::SyncRbTreeVecIntervalRwLock, state};

let vec = Box::pin(SyncRbTreeVecIntervalRwLock::new(vec![0u8; 64])); let vec = vec.as_ref();

// Borrow vec[0..32] state!(let mut state); let guard1 = vec.read(0..32, (), state);

// Borrow vec[16..32] state!(let mut state); let guard2 = vec.read(16..32, (), state);

// Mutably borrow vec[16..48] unsuccessfully state!(let mut state); vec.trywrite(16..48, Pin::asmut(&mut state)).unwrap_err();

// Unborrow vec[0..32] completely drop(guard1); drop(guard2);

// Mutably borrow vec[16..48] vec.trywrite(16..48, Pin::asmut(&mut state)).unwrap(); ```

```rust use parking_lot::RawMutex; use interlock::{hl::slice::AsyncRbTreeVecIntervalRwLock, state};

[tokio::main]

async fn main() { let vec = Box::pin(AsyncRbTreeVecIntervalRwLock::::new(vec![0u8; 64])); let vec = vec.as_ref();

state!(let mut state);
let _guard = vec.async_read(0..32, (), state).await;

state!(let mut state);
let _guard = vec.async_read(16..48, (), state).await;

} ```

Design

Red-black tree based implementation

| Locking Interface | Storage | Type Alias | |-------------------------------|------------|---------------------------------------------------------| | Fallible + Panicking, !Sync | &mut [T] | [crate::hl::slice::LocalRbTreeSliceRefIntervalRwLock] | | ↑ | Vec<T> | [crate::hl::slice::LocalRbTreeVecIntervalRwLock] | | Fallible + Blocking | &mut [T] | [crate::hl::slice::SyncRbTreeSliceRefIntervalRwLock] | | ↑ | Vec<T> | [crate::hl::slice::SyncRbTreeVecIntervalRwLock] | | Fallible + Future-oriented | &mut [T] | [crate::hl::slice::AsyncRbTreeSliceRefIntervalRwLock] | | ↑ | Vec<T> | [crate::hl::slice::AsyncRbTreeVecIntervalRwLock] |

They are modeled as an array of virtual readers-writer locks. When locking, the virtual locks in the specified range are locked in ascending order. When unlocking, they are unlocked in descending order. (The locking order is important to prevent deadlocks.) The wait list of each virtual lock is ordered by (priority, sequence), where sequence is a monotonically increasing number (thus enforcing FIFO ordering). When a virtual lock is unlocked, all entries in the wait list are examined and resumed instantly (if possible) in order.

On a lock conflict, the fallible interface fails and leaves all virtual locks unchanged. Other interfaces maintain the incomplete borrow until it's cancelled or completed by the removal of a conflicting borrow.

The Future-oriented interface supports cancellation.

The worst-case time complexity is shown below:

| Operation | Time Complexity | |-----------|----------------------------------------------------------------| | Locking | O(log(existing_borrows)) | | Unlocking | O((1 + potentially_resumed_borrows) * log(existing_borrows)) |

The space complexity is O(existing_borrows).

Cargo features

Future directions

Alternatives

interlock is surprisingly niche. While it may scale well for large-scale inputs, its complexity and overhead are likely to outweigh the benefit in many practical situations. Consider the following alternatives before choosing interlock: