Rust 100 Ex 🏃【32/37】 `Send`・排他的ロック(`Mutex`)・非対称排他的ロック(`RwLock`) ～真打 `Arc<Mutex<T>>` 登場～

前の記事

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1) ← 初回
- ...
- [【31】 上限付きチャネル・PATCH機能 ～パンクしないように制御！～](https://qiita.com/namn1125/items/d7ae8a205ee969339401) ← 前回
- 【32】 `Send`・排他的ロック(`Mutex`)・非対称排他的ロック(`RwLock`) ～真打 `Arc<Mutex<T>>` 登場～ ← 今回

<details><summary>全記事一覧</summary>

- [【0】 準備](https://qiita.com/namn1125/items/bd1b4cd028874189a3c1)
- [【1】 構文・整数・変数](https://qiita.com/namn1125/items/c776768baec89b8a0438)
- [【2】 if・パニック・演習](https://qiita.com/namn1125/items/13870965e8273bc87bce)
- [【3】 可変・ループ・オーバーフロー](https://qiita.com/namn1125/items/4f57f8aede2f5201016f)
- [【4】 キャスト・構造体 (たまにUFCS)](https://qiita.com/namn1125/items/227ed5bbedbb775c5d27)
- [【5】 バリデーション・モジュールの公開範囲 ～ → カプセル化！～](https://qiita.com/namn1125/items/46721cceccbfa9ec3c7a)
- [【6】 カプセル化の続きと所有権とセッター ～そして不変参照と可変参照！～](https://qiita.com/namn1125/items/442de7798d49f777384c)
- [【7】 スタック・ヒープと参照のサイズ ～メモリの話～](https://qiita.com/namn1125/items/780d80e4340f0e34dbf6)
- [【8】 デストラクタ(変数の終わり)・トレイト ～終わりと始まり～](https://qiita.com/namn1125/items/7247edde3b6bd0b85b25)
- [【9】 Orphan rule (孤児ルール)・演算子オーバーロード・derive ～Empowerment 💪 ～](https://qiita.com/namn1125/items/e565713b423301a80c76)
- [【10】 トレイト境界・文字列・Derefトレイト ～トレイトのアレコレ～](https://qiita.com/namn1125/items/9060faee2965f23e0e3d)
- [【11】 Sized トレイト・From トレイト・関連型 ～おもしろトレイトと関連型～](https://qiita.com/namn1125/items/b68990712d379a3d6a49)
- [【12】 Clone・Copy・Dropトレイト ～覚えるべき主要トレイトたち～](https://qiita.com/namn1125/items/c6f34945f331c7229746)
- [【13】 トレイトまとめ・列挙型・match式 ～最強のトレイトの次は、最強の列挙型～](https://qiita.com/namn1125/items/6b2cc31d6a47f9ffaff6)
- [【14】 フィールド付き列挙型とOption型 ～チョクワガタ～](https://qiita.com/namn1125/items/6fe69d1cab6c7936c698)
- [【15】 Result型 ～Rust流エラーハンドリング術～](https://qiita.com/namn1125/items/698aafaf22ec4bfd1bde)
- [【16】 Errorトレイトと外部クレート ～依存はCargo.tomlに全部お任せ！～](https://qiita.com/namn1125/items/9dae7b728fa0668e4102)
- [【17】 thiserror・TryFrom ～トレイトもResultも自由自在！～](https://qiita.com/namn1125/items/d67006a29b7ff5d6148c)
- [【18】 Errorのネスト・慣例的な書き方 ～Rustらしさの目醒め～](https://qiita.com/namn1125/items/705829027661a38e4a4f)
- [【19】 配列・動的配列 ～スタックが使われる配列と、ヒープに保存できる動的配列～](https://qiita.com/namn1125/items/9c49fd4c08f3e61896c7)
- [【20】 動的配列のリサイズ・イテレータ ～またまたトレイト登場！～](https://qiita.com/namn1125/items/6e4dede789c6de97f396)
- [【21】 イテレータ・ライフタイム ～ライフタイム注釈ようやく登場！～](https://qiita.com/namn1125/items/9a1224abf733f29a980a)
- [【22】 コンビネータ・RPIT ～ :dragon: 「 `Iterator` トレイトを実装してるやつ」～](https://qiita.com/namn1125/items/27fe20f2d60803c94357)
- [【23】 `impl Trait` ・スライス ～配列の欠片～](https://qiita.com/namn1125/items/7601d0268293ce7fcf28)
- [【24】 可変スライス・下書き構造体 ～構造体で状態表現～](https://qiita.com/namn1125/items/a9acc7dc2ca7c4c55cc3)
- [【25】 インデックス・可変インデックス ～インデックスもトレイト！～](https://qiita.com/namn1125/items/bd43375362af10ea8b42)
- [【26】 HashMap・順序・BTreeMap ～Rustの辞書型～](https://qiita.com/namn1125/items/a2806cded12fa719a1cb)
- [【27】 スレッド・'staticライフタイム ～並列処理に見るRustの恩恵～](https://qiita.com/namn1125/items/5a4a09172834063268b1)
- [【28】 リーク・スコープ付きスレッド ～ライフタイムに技あり！～](https://qiita.com/namn1125/items/34a0aacd4d40f9234aef)
- [【29】 チャネル・参照の内部可変性 ～Rustの虎の子、mpscと `Rc<RefCell<T>>`～](https://qiita.com/namn1125/items/861e72dbb4b837508948)
- [【30】 双方向通信・リファクタリング ～返信用封筒を入れよう！～](https://qiita.com/namn1125/items/46f03de967d0de46737e)
- [【31】 上限付きチャネル・PATCH機能 ～パンクしないように制御！～](https://qiita.com/namn1125/items/d7ae8a205ee969339401)
- [【32】 `Send`・排他的ロック(`Mutex`)・非対称排他的ロック(`RwLock`) ～真打 `Arc<Mutex<T>>` 登場～](https://qiita.com/namn1125/items/eee890dc8672e975e70c)
- [【33】 チャネルなしで実装・Syncの話 ～考察回です～](https://qiita.com/namn1125/items/cc8bee3871b727e564e3)
- [【34】 `async fn`・非同期タスク生成 ～Rustの非同期入門～](https://qiita.com/namn1125/items/9c32b730a283ad76c891)
- [【35】 非同期ランタイム・Futureトレイト ～非同期のお作法～](https://qiita.com/namn1125/items/c840d48f8f558613ec10)
- [【36】 ブロッキング・非同期用の実装・キャンセル ～ラストスパート！～](https://qiita.com/namn1125/items/39c139da3f1f26264353)
- [【37】 Axumでクラサバ！ ～最終回～](https://qiita.com/namn1125/items/bae492f823fbe60e9974)
- [【おまけ1】 Rustで勘違いしていたこと3選 🏄🌴 【100 Exercises To Learn Rust 🦀 完走記事 🏃】](https://qiita.com/namn1125/items/2a59f486cce30fd2b889)
- [【おまけ2】 【🙇 懺悔 🙇】Qiitanグッズ欲しさに1日に33記事投稿した話またはQiita CLIとcargo scriptを布教する的な何か](https://qiita.com/namn1125/items/8e552694b389c5b13b5e)
</details>

[100 Exercise To Learn Rust](https://rust-exercises.com/) 演習第32回になります！

今回の関連ページ

- [7.11. Mutex, Send and Arc](https://rust-exercises.com/07_threads/11_locks)
- [7.12. RwLock](https://rust-exercises.com/07_threads/12_rw_lock)

# [[07_threads/11_locks](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/11_locks/src/lib.rs)] 排他的アクセス

- [7.11. Mutex, Send and Arc](https://rust-exercises.com/07_threads/11_locks)

## [前回](https://qiita.com/namn1125/items/d7ae8a205ee969339401)エクササイズの問題点と、その解決策ミューテックス [`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)

前回エクササイズでは単にパッチを投げてそれをサーバー側が受け取り更新処理としていました。

今回の[Book](https://rust-exercises.com/100-exercises/07_threads/11_locks)の最初にて指摘されている通り、こちらは「競合状態(レースコンディション)」の問題があります。2つのクライアントがほぼ同時に同じチケットに対する更新処理を送ってきたら、先に来た片方のクライアントの更新は虚無に消えてしまいます！

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor S  as サーバー
    actor C2 as クライアント2
    C1->>S: Get { id: 1 }
    S->>C1: Ticket { id: 1 }
    C2->>S: Get { id: 1 }
    S->>C2: Ticket { id: 1 }
    C1->>S: Update { patch: TicketPatch { id: 1 } }
    C2->>S: Update { patch: TicketPatch { id: 1 } }
    Note left of S: 😵 クライアント1の更新は虚無に！
```

2つ解決策が思い浮かび、一つはバージョンで管理する方法です。

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor S  as サーバー
    actor C2 as クライアント2
    C1->>S: Get { id: 1 }
    S->>C1: Ticket { id: 1, ver: 1 }
    C2->>S: Get { id: 1 }
    S->>C2: Ticket { id: 1, ver: 1 }
    C1->>S: Update { patch: TicketPatch { id: 1, ver: 1 } }
    Note right of S: 今後はver: 2を受け入れます
    C2-xS: Update { patch: TicketPatch { id: 1, ver: 1 } }
    Note left of S: ❌️クライアント2の更新を棄却！
```

「楽観的並行処理」として、こちらは任意課題扱いになっていますね。分散システムでもよく見られるパターンですし、自分も[最終回](https://qiita.com/namn1125/items/bae492f823fbe60e9974)での実装にてこちらの方針を採用しています！

しかし、今回はもう一つの解決策を取ります。「**ロック**方式」です！変更を加えたいチケットに対して、チケットではなくそのチケットのロックである[`Mutex`](https://doc.rust-lang.org/std/sync/struct.Mutex.html)`<Ticket>` (MUTual EXclusion, ミューテックス, 相互排他)を取り、ロック中は他のクライアントからはアクセスできないようにします。ロックはチケットごとに用意してあり、今回サーバーからもらえるのはこのチケットへの参照( [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)`<Mutex<Ticket>>` )です！

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    participant T1 as チケット id: 1
    actor S  as サーバー
    actor C2 as クライアント2
    C1->>S: Get { id: 1 }
    S->>C1: チケット id: 1 への Arc
    C2->>S: Get { id: 1 }
    S->>C2: チケット id: 1 への Arc
    C1->>T1: ロック獲得
    activate T1
    Note over T1: クライアント1がロック中
    C2-xT1: ❌️ロック獲得できず！
    activate C2
    Note over C2: ロック獲得できるまで待機    
    Note over C1, T1: 変更を加えるなど
    C1->>T1: ロック解除！
    deactivate T1
    C2->>T1: ロック獲得
    deactivate C2
    activate T1
    Note over T1: クライアント2がロック中
    Note over C2, T1: 変更を加えるなど
    C2->>T1: ロック解除！
    deactivate T1
```

というわけで、演習問題は上図の実現です！

## 問題

前置きが長くなりましたが、今回の問題はこちらです。src/lib.rs の方はご丁寧にも `Arc<Mutex<T>>` を使用するようにすでに改良が施されているため、 src/store.rs のみ改変することになります。

src/lib.rs には何も改変を加える必要はありませんが、今回問題に関わる部分を `diff_rust` マークアップでハイライトしておきます。追加行ではありません。

```diff_rust:src/lib.rs
// TODO: Fill in the missing methods for `TicketStore`.
//  Notice how we no longer need a separate update command: `Get` now returns a handle to the ticket
//  which allows the caller to both modify and read the ticket.
use std::sync::mpsc::{sync_channel, Receiver, SyncSender, TrySendError};
+ use std::sync::{Arc, Mutex};

use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};

pub mod data;
pub mod store;

#[derive(Clone)]
pub struct TicketStoreClient {
    sender: SyncSender<Command>,
}

impl TicketStoreClient {
    pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, OverloadedError> {
        let (response_sender, response_receiver) = sync_channel(1);
        self.sender
            .try_send(Command::Insert {
                draft,
                response_channel: response_sender,
            })
            .map_err(|_| OverloadedError)?;
        Ok(response_receiver.recv().unwrap())
    }

+     pub fn get(&self, id: TicketId) -> Result<Option<Arc<Mutex<Ticket>>>, OverloadedError> {
        let (response_sender, response_receiver) = sync_channel(1);
        self.sender
            .try_send(Command::Get {
                id,
                response_channel: response_sender,
            })
            .map_err(|_| OverloadedError)?;
        Ok(response_receiver.recv().unwrap())
    }
}

#[derive(Debug, thiserror::Error)]
#[error("The store is overloaded")]
pub struct OverloadedError;

pub fn launch(capacity: usize) -> TicketStoreClient {
    let (sender, receiver) = sync_channel(capacity);
    std::thread::spawn(move || server(receiver));
    TicketStoreClient { sender }
}

enum Command {
    Insert {
        draft: TicketDraft,
        response_channel: SyncSender<TicketId>,
    },
    Get {
        id: TicketId,
+         response_channel: SyncSender<Option<Arc<Mutex<Ticket>>>>,
    },
}

pub fn server(receiver: Receiver<Command>) {
    let mut store = TicketStore::new();
    loop {
        match receiver.recv() {
            Ok(Command::Insert {
                draft,
                response_channel,
            }) => {
                let id = store.add_ticket(draft);
                let _ = response_channel.send(id);
            }
            Ok(Command::Get {
                id,
                response_channel,
            }) => {
                let ticket = store.get(id);
                let _ = response_channel.send(ticket);
            }
            Err(_) => {
                // There are no more senders, so we can safely break
                // and shut down the server.
                break;
            }
        }
    }
}
```

```diff_rust:src/store.rs
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
+ use std::sync::{Arc, Mutex};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);

#[derive(Clone)]
pub struct TicketStore {
+     tickets: BTreeMap<TicketId, Arc<Mutex<Ticket>>>,
    counter: u64,
}

impl TicketStore {
    pub fn new() -> Self {
        Self {
            tickets: BTreeMap::new(),
            counter: 0,
        }
    }

    pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
        let id = TicketId(self.counter);
        self.counter += 1;
        let ticket = Ticket {
            id,
            title: ticket.title,
            description: ticket.description,
            status: Status::ToDo,
        };
        todo!();
        id
    }

    // The `get` method should return a handle to the ticket
    // which allows the caller to either read or modify the ticket.
    pub fn get(&self, id: TicketId) -> Option<todo!()> {
        todo!()
    }
}
```

<details><summary>その他のファイル</summary>

```rust:src/data.rs
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};

#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
    pub title: TicketTitle,
    pub description: TicketDescription,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}
```

```rust:tests/check.rs
use locks::data::{Status, TicketDraft};
use locks::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};

#[test]
fn works() {
    let client = launch(5);
    let draft = TicketDraft {
        title: ticket_title(),
        description: ticket_description(),
    };
    let ticket_id = client.insert(draft.clone()).unwrap();

    let ticket = client.get(ticket_id).unwrap().unwrap();
    {
        let mut ticket = ticket.lock().unwrap();
        assert_eq!(ticket_id, ticket.id);
        assert_eq!(ticket.status, Status::ToDo);
        assert_eq!(ticket.title, draft.title);
        assert_eq!(ticket.description, draft.description);

        ticket.status = Status::InProgress;
    }

    let ticket = client.get(ticket_id).unwrap().unwrap();
    {
        let ticket = ticket.lock().unwrap();
        assert_eq!(ticket_id, ticket.id);
        assert_eq!(ticket.status, Status::InProgress);
    }
}
```
</details>

## 解説

`Arc<Mutex<Ticket>>` を使用するように改変します！

```rust:lib.rs
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);

#[derive(Clone)]
pub struct TicketStore {
    tickets: BTreeMap<TicketId, Arc<Mutex<Ticket>>>,
    counter: u64,
}

impl TicketStore {
    pub fn new() -> Self {
        Self {
            tickets: BTreeMap::new(),
            counter: 0,
        }
    }

    pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
        let id = TicketId(self.counter);
        self.counter += 1;
        let ticket = Ticket {
            id,
            title: ticket.title,
            description: ticket.description,
            status: Status::ToDo,
        };
        self.tickets.insert(id, Arc::new(Mutex::new(ticket)));
        id
    }

    // The `get` method should return a handle to the ticket
    // which allows the caller to either read or modify the ticket.
    pub fn get(&self, id: TicketId) -> Option<Arc<Mutex<Ticket>>> {
        self.tickets.get(&id).cloned()
    }
}
```

`add_thicket` メソッドにて `BTreeMap::insert` に格納する際には、 `Arc<Mutex<Ticket>>` 型になるようにここで `Arc` と `Mutex` を作成しています。

`get` メソッドの方は、以前は `&Ticket` を返せばよかったので `get(&id)` で止まっていました。今回は `Arc<Mutex<Ticket>>` を返したいです。 `get(&id)` は結果が `Some` なら中身が `&Arc<...>` となっているため、この時だけ `Arc::clone (Clone::clone)` が呼ばれるように [`cloned` メソッド](https://doc.rust-lang.org/std/option/enum.Option.html#method.cloned) を使い、 `Arc` を複製して他スレッドに渡せるようにします！

そして `get` でチケットへのハンドラを扱えるようになったため、前回追加した `Update` コマンド及びそれに関連する処理、 `get_mut` メソッドはもはや不要になりました！今後は、クライアントが勝手にロックして中身を書き換えられます。

## `Send` と `Rc<RefCell<T>>` ・ `Arc<Mutex<T>>` 関係早見表

ここで「 `Arc<Mutex<T>>` は [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html)`<`[`RefCell<T>`](https://doc.rust-lang.org/std/cell/struct.RefCell.html)`>` とほぼ同じ機能では...？」という定番質問があります。「マルチスレッドで使える([**`Send`**](https://doc.rust-lang.org/std/marker/trait.Send.html) や [`Sync`](https://doc.rust-lang.org/stable/std/marker/trait.Sync.html) が付いているのでスレット跨ぎができる)のが `Arc<Mutex<T>>` 、使えないのが  `Rc<RefCell<T>>` 」というのが簡単な回答になります。

`Rc` の内部カウンターは本記事1枚目の図のようなレースコンディションを起こしてしまう仕組みになってしまっており、 `Arc` はカウント操作をアトミックにすることでそれを克服しています。このスレッド間転送可能かどうかの印として、 `Send` マーカートレイトが用いられます。 `Send` がなければスレッドを跨ぐクロージャに渡すことができません！

両者の違いや `Send` マーカートレイトの詳細について [Book](https://rust-exercises.com/100-exercises/07_threads/11_locks)、[TRPL](https://doc.rust-jp.rs/book-ja/ch16-03-shared-state.html)どちらにも説明があるので、解説はこちらに譲り、この先 `Sync` が登場することも踏まえ代わりに「Send・Sync早見表」を設置しておきます！

- `Send`: 本体を別スレッドに送れる
- `Sync`: 本体への参照を別スレッドに送れる
  - つまり `impl Sync for T` と `impl Send for &T` が同値
  - 意味的な部分は [次回](https://qiita.com/namn1125/items/cc8bee3871b727e564e3) 扱う予定

:o: : `Send`, `Sync`
:x: : `!Send`, `!Sync`

**ローカルスレッド向け**

| 型                                                                | Send  | Sync  | 機能                                                                                                                              |
| :---------------------------------------------------------------- | :---: | :---: | :-------------------------------------------------------------------------------------------------------------------------------- |
| [Rc](https://doc.rust-lang.org/std/rc/struct.Rc.html)             |  :x:  |  :x:  | 所有権シェア                                                                                                                      |
| [RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html) |  :o:  |  :x:  | 動的可変参照[^refcell]                                                                                                            |
| [Ref](https://doc.rust-lang.org/std/cell/struct.Ref.html)         |  :x:  |  :x:  | [`RefCell::<T>::borrow`](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.borrow) で得られる [`Deref<Target = T>`](https://doc.rust-lang.org/std/ops/trait.Deref.html)                       |
| [RefMut](https://doc.rust-lang.org/std/cell/struct.RefMut.html)   |  :x:  |  :x:  | [`RefCell::<T>::borrow_mut`](https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.borrow_mut) で得られる [`DerefMut<Target = T>`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html) [^derefmut] |

[^refcell]: コンパイル時は不変参照だけど、ランタイム時に可変になれることより筆者が付けた呼称です。[29回](https://qiita.com/namn1125/items/861e72dbb4b837508948)で扱ったように「不変参照に内部可変性を付与し、借用チェックをランタイムで行う機能」とか説明したほうがもう少し正確かもしれません。
[^derefmut]: `Target = T` は前提トレイトの `Deref` から得られるもので、直接持っている関連型ではありませんが便宜上このように記載しています。

**マルチスレッド向け**

|   型 | Send  | Sync  | 機能 |
| :--- | :---: | :---: | :--- |
| [Arc](https://doc.rust-lang.org/std/sync/struct.Arc.html)     | :o:      | :o:      | 所有権シェア     |
| [Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html) | :o: | :o: | 排他アクセス機能 |
| [MutexGuard](https://doc.rust-lang.org/stable/std/sync/struct.MutexGuard.html) | :x: | :o: | [`Mutex::<T>::lock`](https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.lock) で得られる `Deref<Target = T>` & `DerefMut` |

`RefCell` が :o: :x: で `MutexGuard` が :x: :o: なのが興味深いですね...これについては[次回](https://qiita.com/namn1125/items/cc8bee3871b727e564e3)にて考察したいと思います[^sendsynckaisetsu]！

[^sendsynckaisetsu]: メタいことを言えばここで解説しちゃうと次回話すネタがなくなってしまうのです...

`RefCell` と `Mutex` には挙動にも違いがあります。 `RefCell` はローカルスレッドでしかアクセスを許さない代わりに、可変参照・不変参照の制約をランタイム時に調べてくれて、満たさない場合パニックします( `try_borrow*` なら `Result` 化可能)。一方 `Mutex` の方は不変・可変を問わず「ロックを取ります」。このロックというのが厄介で、ロック中にロックを得ようとするとローカルスレッド内ですら(つまり `RefCell` と[同様の使い方](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e13197b4fa6bee7489e8513f86d8f768)をした場合でも)パニックしてくれず **デッドロックします** [^deadlock]。つまり扱いとしては `Mutex` の方が気をつけるべき点が多く[^caution]、むしろ違いとしてはこちらの方が大きそうですね。

[^deadlock]: [公式ドキュメントの解説](https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.lock:~:text=The%20exact%20behavior%20on%20locking%20a%20mutex%20in%20the%20thread%20which%20already%20holds%20the%20lock%20is%20left%20unspecified.%20However%2C%20this%20function%20will%20not%20return%20on%20the%20second%20call%20(it%20might%20panic%20or%20deadlock%2C%20for%20example).)だとパニックしてくれる場合もあるらしい(とにかく、値が返らないという点は変わらないと書いてある)ですが、[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e13197b4fa6bee7489e8513f86d8f768) で示した環境だとデッドロックしました

[^caution]: ランタイム時に想定していない挙動をするという点では `RefCell` でも `Mutex` でも変わらないですし、 `RefCell` はそもそも使い所がイマイチなことが多いので、総括すると扱うにあたる体感の慎重さは両者間で変わらない気がします。

:::note 
**`Arc<Mutex<T>>` は万能...？**

ある値を取り扱うのに、いままでは所有権で苦しんでいたところに、 `Arc` のお陰で「複数のスレッド・変数を跨いで共有できる」し、 `Mutex` のお陰で「複数の変数から変更可能」になりました。これで他言語の"変数"とようやくトントンに色々できそうです！safe Rustの範囲で可変なグローバル変数のように扱うことも頑張ればできそうです。

そして、万能かと聞かれるとデッドロックするから答えは「No」になるのですが、つまるところ、「万能な可変グローバル変数」なんてものは人類が扱ってはいけないことを、長い型名と`Mutex` のデッドロックしてしまう性質から教えてくれているんじゃないかと思います。

Rustの考えが豊富に詰め込まれているという点では、 `Arc<Mutex<T>>` は "真打" 感がありますね。適切なシーンで使えるようになりたいです(筆者の経験則的にはできるだけ [`mpsc`](https://doc.rust-lang.org/nightly/std/sync/mpsc/index.html) を利用するのが正解...)。
:::

# [[07_threads/12_rw_lock](https://github.com/mainmatter/100-exercises-to-learn-rust/blob/main/exercises/07_threads/12_rw_lock/src/lib.rs)] 非対称排他的アクセス ( `RwLock` )

- [7.12. RwLock](https://rust-exercises.com/07_threads/12_rw_lock)

`Mutex` は読み書き両方を含めたロックしか取れませんが、 `RwLock` ならば(`&` のように)複数アクセスを認める読み取り専用ロックと、(`&mut` のように)専有アクセスしか認めない読み書きロックという器用な2つのロックを使い分けることができ、より `RefCell` と似た感じに扱えます！ `Mutex` と `RwLock` の良し悪しは [Book](https://rust-exercises.com/100-exercises/07_threads/12_rw_lock) に書かれているのでそちらに譲ります。

**マルチスレッド向け** (続き)

|   型 | Send  | Sync  | 機能 |
| :--- | :---: | :---: | :--- |
| [RwLock](https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html) | :o: | :o: | `Mutex` において、読み書き専有ロック(`RwLockWriteGuard`)か読み込み共有ロック(`RwLockReadGuard`)かを選べるようにしたもの |
| [RwLockWriteGuard](https://doc.rust-lang.org/stable/std/sync/struct.RwLockWriteGuard.html) | :x: | :o: | [`LwLock::<T>::read`](https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#method.read) で得られる `Deref<Target = T>` |
| [RwLockReadGuard](https://doc.rust-lang.org/stable/std/sync/struct.RwLockReadGuard.html) | :x: | :o: | [`LwLock::<T>::write`](https://doc.rust-lang.org/stable/std/sync/struct.RwLock.html#method.write) で得られる `DerefMut<Target = T>` [^derefmut] |

```mermaid
sequenceDiagram
    actor C1 as クライアント1
    actor C2 as クライアント2
    participant T1 as チケット id: 1
    actor S  as サーバー
    actor C3 as クライアント3
    C1->>S: Get { id: 1 }
    S->>C1: チケット id: 1 への Arc
    C2->>S: Get { id: 1 }
    S->>C2: チケット id: 1 への Arc
    C3->>S: Get { id: 1 }
    S->>C3: チケット id: 1 への Arc
    C1->>T1: 読み取りロック獲得
    activate T1
    C2->>T1: 読み取りロック獲得
    Note over T1: クライアント1, 2がReadロック中
    C3-xT1: ❌️読み書きロック獲得できず！
    activate C3
    Note over C3: 読み書きロック獲得できるまで待機
    C1->>T1: ロック解除！
    C2->>T1: ロック解除！
    deactivate T1
    C3->>T1: 読み書きロック獲得
    deactivate C3
    activate T1
    Note over T1: クライアント3がロック中
    C1-xT1: ❌️読み取りロック獲得できず！
    C3->>T1: ロック解除！
    deactivate T1
```

## 問題・解説

**`Mutex` を `RwLock` に置換する必要があるだけでほとんど前問と変わりません** 。 `tests/check.rs` で呼び出しているメソッドには変化があります。

<details><summary>一応ファイル (回答済み)</summary>

```rust:src/lib.rs
// TODO: Replace `Mutex` with `RwLock` in the `TicketStore` struct and
//  all other relevant places to allow multiple readers to access the ticket store concurrently.
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
use std::sync::{Arc, RwLock};

use crate::data::{Ticket, TicketDraft};
use crate::store::{TicketId, TicketStore};

pub mod data;
pub mod store;

#[derive(Clone)]
pub struct TicketStoreClient {
    sender: SyncSender<Command>,
}

impl TicketStoreClient {
    pub fn insert(&self, draft: TicketDraft) -> Result<TicketId, OverloadedError> {
        let (response_sender, response_receiver) = sync_channel(1);
        self.sender
            .try_send(Command::Insert {
                draft,
                response_channel: response_sender,
            })
            .map_err(|_| OverloadedError)?;
        Ok(response_receiver.recv().unwrap())
    }

    pub fn get(&self, id: TicketId) -> Result<Option<Arc<RwLock<Ticket>>>, OverloadedError> {
        let (response_sender, response_receiver) = sync_channel(1);
        self.sender
            .try_send(Command::Get {
                id,
                response_channel: response_sender,
            })
            .map_err(|_| OverloadedError)?;
        Ok(response_receiver.recv().unwrap())
    }
}

#[derive(Debug, thiserror::Error)]
#[error("The store is overloaded")]
pub struct OverloadedError;

pub fn launch(capacity: usize) -> TicketStoreClient {
    let (sender, receiver) = sync_channel(capacity);
    std::thread::spawn(move || server(receiver));
    TicketStoreClient { sender }
}

enum Command {
    Insert {
        draft: TicketDraft,
        response_channel: SyncSender<TicketId>,
    },
    Get {
        id: TicketId,
        response_channel: SyncSender<Option<Arc<RwLock<Ticket>>>>,
    },
}

fn server(receiver: Receiver<Command>) {
    let mut store = TicketStore::new();
    loop {
        match receiver.recv() {
            Ok(Command::Insert {
                draft,
                response_channel,
            }) => {
                let id = store.add_ticket(draft);
                let _ = response_channel.send(id);
            }
            Ok(Command::Get {
                id,
                response_channel,
            }) => {
                let ticket = store.get(id);
                let _ = response_channel.send(ticket);
            }
            Err(_) => {
                // There are no more senders, so we can safely break
                // and shut down the server.
                break;
            }
        }
    }
}
```

```rust:src/data.rs
use crate::store::TicketId;
use ticket_fields::{TicketDescription, TicketTitle};

#[derive(Clone, Debug, PartialEq)]
pub struct Ticket {
    pub id: TicketId,
    pub title: TicketTitle,
    pub description: TicketDescription,
    pub status: Status,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TicketDraft {
    pub title: TicketTitle,
    pub description: TicketDescription,
}

#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum Status {
    ToDo,
    InProgress,
    Done,
}
```

```rust:src/store.rs
use crate::data::{Status, Ticket, TicketDraft};
use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct TicketId(u64);

#[derive(Clone)]
pub struct TicketStore {
    tickets: BTreeMap<TicketId, Arc<RwLock<Ticket>>>,
    counter: u64,
}

impl TicketStore {
    pub fn new() -> Self {
        Self {
            tickets: BTreeMap::new(),
            counter: 0,
        }
    }

    pub fn add_ticket(&mut self, ticket: TicketDraft) -> TicketId {
        let id = TicketId(self.counter);
        self.counter += 1;
        let ticket = Ticket {
            id,
            title: ticket.title,
            description: ticket.description,
            status: Status::ToDo,
        };
        let ticket = Arc::new(RwLock::new(ticket));
        self.tickets.insert(id, ticket);
        id
    }

    // The `get` method should return a handle to the ticket
    // which allows the caller to either read or modify the ticket.
    pub fn get(&self, id: TicketId) -> Option<Arc<RwLock<Ticket>>> {
        self.tickets.get(&id).cloned()
    }
}
```

```rust:tests/check.rs
use rwlock::data::{Status, TicketDraft};
use rwlock::launch;
use ticket_fields::test_helpers::{ticket_description, ticket_title};

#[test]
fn works() {
    let client = launch(5);
    let draft = TicketDraft {
        title: ticket_title(),
        description: ticket_description(),
    };
    let ticket_id = client.insert(draft.clone()).unwrap();

    let ticket = client.get(ticket_id).unwrap().unwrap();
    let lock1 = ticket.read().unwrap();
    {
        let ticket = ticket.read().unwrap();
        assert_eq!(ticket_id, ticket.id);
        assert_eq!(ticket.status, Status::ToDo);
        assert_eq!(ticket.title, draft.title);
        assert_eq!(ticket.description, draft.description);
    }

    drop(lock1);

    let ticket = client.get(ticket_id).unwrap().unwrap();
    {
        let mut ticket = ticket.write().unwrap();
        ticket.status = Status::InProgress;
    }
}

```
</details>

では次の問題に行きましょう！

次の記事: [【33】 チャネルなしで実装・Syncの話 ～考察回です～](https://qiita.com/namn1125/items/cc8bee3871b727e564e3)

<details><summary>登場したPlayground</summary>

(実際に無効化したことはないですが、)Rust Playground上のデータが喪失する可能性を鑑みて、一応記事にもソースコードを掲載することとしました。

URL: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e13197b4fa6bee7489e8513f86d8f768

```rust:Rust
use std::sync::{Arc, Mutex, MutexGuard};
use std::fmt::Debug;
use std::cell::{RefCell, RefMut};
use std::rc::Rc;

pub fn get_lock_rrr<T: Debug>(rrr: Rc<RefCell<T>>, rec: bool) {
    let rrr2 = Rc::clone(&rrr);
    
    let rfmt: RefMut<'_, T> = rrr.borrow_mut();

    if rec {
        get_lock_rrr(rrr2, false);
    }
    
    println!("{:?}", *rfmt);
}

#[test]
#[should_panic(expected = "already borrowed")]
fn rrr_test() {
    let rrr = Rc::new(RefCell::new(10));
    
    get_lock_rrr(rrr, true);
}

pub fn get_lock_amr<T: Debug>(amr: Arc<Mutex<T>>, rec: bool) {
    #[allow(unused_variables)]
    let amr2 = Arc::clone(&amr);

    let guard: MutexGuard<'_, T> = amr.lock().unwrap();

    if rec {
        // コメントを外すとデッドロック
        // get_lock_amr(amr2, false);
    }
    
    println!("{:?}", *guard);
}

#[test]
fn amr_test() {
    let amr = Arc::new(Mutex::new(10));
    
    get_lock_amr(amr, true);
}
```
</details>