rustのMutexの内部構造

前回に引き続き,今度はMutexの話です.

Raph Levien氏の図を引用すると,Mutexは以下のような構造になっています.

f:id:mm_i:20190213182929p:plain
Raph Levien, Copyright 2017 Google Inc., released under Creative Commons BY

また,データ構造としては以下のように定義されています(一部抜粋).

pub struct Mutex<T: ?Sized> {
    inner: Box<sys::Mutex>,
    poison: poison::Flag,
    data: UnsafeCell<T>,
}

ここから,以下のことが分かります.

  1. Mutex自体はデータをヒープに置かない
  2. mutex本体(sys::Mutex)は,ヒープに置かれる

sys::Mutexは,*nix環境ではpthread_mutex_tです. したがって,Mutexのlock/unlockはpthread_mutex_lock()およびpthread_mutex_unlock()に対応しています. mutex本体をヒープに置くのは,OSによっては(pthreadが?)mutex本体のアドレスが変わらないことを前提としているからだそうです(このあたりの実装は未確認). ヒープに置いておかないと,Mutexをmoveしたときmutex本体のアドレスが変わってしまいます.

ちなみに,RwLockMutexとほぼ同じ構造をしています.

https://github.com/rust-lang/rust/blob/1.32.0/src/libstd/sync/rwlock.rs

pub struct RwLock<T: ?Sized> {
    inner: Box<sys::RWLock>,
    poison: poison::Flag,
    data: UnsafeCell<T>,
}

Mutexを利用するときはマルチスレッドで何かしたい場合なので,実際に利用する場合は,Arcと組み合わせてArc<Mutex<T>>のような形で利用されることが多いと思います.

parking_lot

さて,上のMutexの図には"parking_lotを使ってみたら?"と書いてあります. parking_lotはスピンロックを使ったmutexを提供します.

以下のような感じでMutexが定義されます(一部抜粋).

// lock_api/src/mutex.rc
pub struct Mutex<R: RawMutex, T: ?Sized> {
    raw: R,
    data: UnsafeCell<T>,
}

// src/mutex.rc
type Mutex<T> = lock_api::Mutex<RawMutex, T>;

// src/raw_mutex.rs
pub struct RawMutex {
    state: AtomicU8,
}

unsafe impl RawMutexTrait for RawMutex {
    ...
    #[inline]
    fn lock(&self) {
        if self
            .state
            .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
            .is_err()
        {
            self.lock_slow(None);
        }
        unsafe { deadlock::acquire_resource(self as *const _ as usize) };
    }
    ...
}

stdのMutexと比較すると,以下の特徴があります.

  • pthreadではなくて(adaptive)スピンロックを利用する
  • mutex本体をヒープに割り当てる必要がない
  • poisonフィールドもない
    • このフィールドは,ロックを保持したスレッドがpanicしたかどうか検出するためのもの
    • parking_lotの場合,panicしたらlockは解放されるようになっている

さて,こんなparking_lotですが,ちょうど今parking_lotをlibstdに取り込もうという動きがあるようです.

深く議論は追っていませんが,しばらくするとstdに追加されてるかもしれません(というか,OS nativeなmutexの代わりにバックエンドとして利用されることになりそう?).