#mutex #access #orchestrator #lock #separate #guard #acquire

orchestrator_lock

A Mutex whose access order is managed by a separate thread

4 releases

Uses new Rust 2024

new 0.2.1 Mar 13, 2025
0.2.0 Mar 12, 2025
0.1.1 Mar 11, 2025
0.1.0 Mar 7, 2025

#575 in Concurrency

Download history 84/week @ 2025-03-01 367/week @ 2025-03-08

451 downloads per month

MIT license

20KB
334 lines

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() {
    // Create a shared counter with initial value 0
    let mut orchestrator = OrchestratorMutex::new(0);
    
    // Create two granter/locker pairs
    let (mut granter1, mut locker1) = orchestrator.add_locker();
    let (mut granter2, mut locker2) = orchestrator.add_locker();
    
    // Task 1: Increments by 1 each time
    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
    });
    
    // Task 2: Multiplies by 2 each time
    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
    });
    
    // Orchestration: Alternate between the two tasks
    for i in 0..3 {
        // Grant access to task 1
        let task1_holding = orchestrator.grant_access(&mut granter1).await.unwrap();
        task1_holding.await;
        
        // Grant access to task 2
        let task2_holding = orchestrator.grant_access(&mut granter2).await.unwrap();
        task2_holding.await;
    }
    assert_eq!(*orchestrator.acquire().await, 14);
    // Clean up
    let _ = task1.await.unwrap();
    let _ = task2.await.unwrap();
}

Dependencies

~2.7–8.5MB
~71K SLoC