orchestrator_lock provides a specialized mutex implementation for scenarios
where fine-grained control over mutex access is required. Unlike a standard
mutex where any code with a reference can attempt to acquire the lock, this
implementation separates the concerns of lock orchestration from lock usage.
Core Concepts
-
OrchestratorMutex: The central coordinator that owns the protected value and
controls access to it.
-
Granter: A capability token that allows the orchestrator to grant lock access
to a specific locker.
-
MutexLocker: The component that can acquire and use the lock, but only when
explicitly granted access by the orchestrator.
-
MutexGuard: Provides access to the protected value, similar to a standard
mutex guard.
Example
use tokio::time::Duration;
use orchestrator_lock::OrchestratorMutex;
#[tokio::main(flavor = "current_thread")]
async fn main() {
let mut orchestrator = OrchestratorMutex::new(0);
let (mut granter1, mut locker1) = orchestrator.add_locker();
let (mut granter2, mut locker2) = orchestrator.add_locker();
let task1 = tokio::spawn(async move {
let expected = [0, 2, 6];
for i in 0..3 {
if let Some(mut guard) = locker1.acquire().await {
assert_eq!(*guard, expected[i]);
*guard += 1;
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
locker1
});
let task2 = tokio::spawn(async move {
let expected = [1, 3, 7];
for i in 0..3 {
if let Some(mut guard) = locker2.acquire().await {
assert_eq!(*guard, expected[i]);
*guard *= 2;
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
locker2
});
let expected = [1, 2, 3, 6, 7, 14];
for i in 0..3 {
let grant_future = orchestrator.grant_access(&mut granter1).await.unwrap();
let guard = grant_future.await;
assert_eq!(*guard, expected[i * 2]);
drop(guard);
let grant_future = orchestrator.grant_access(&mut granter2).await.unwrap();
let guard = grant_future.await;
assert_eq!(*guard, expected[i * 2 + 1]);
drop(guard);
}
let _ = task1.await.unwrap();
let _ = task2.await.unwrap();
}