CodeBlanks Logo Icon CodeBlanks

Arc<Mutex> state

Manage shared state with Arc<Mutex<>>.

Arc<Mutex> state

The chat room's list of who's connected is shared state that every client thread reads and changes. It's shared with Arc<Mutex<Oda>>; each change happens under a short lock:

struct Oda {
    uyeler: Vec<String>,
}

impl Oda {
    fn katil(&mut self, ad: &str) { self.uyeler.push(ad.to_string()); }
    fn ayril(&mut self, ad: &str) { self.uyeler.retain(|k| k != ad); }
}

let oda = Arc::new(Mutex::new(Oda { uyeler: Vec::new() }));

Keeping the methods on Oda separates locking code from business logic: oda.lock().unwrap().katil("ece").

Joining from threads

Each client thread adds its own name to the room. Since the threads' finishing order is undefined, the list is sorted before printing:

let tutucular: Vec<_> = ["ece", "can", "ali"]
    .into_iter()
    .map(|ad| {
        let oda = Arc::clone(&oda);
        thread::spawn(move || oda.lock().unwrap().katil(ad))
    })
    .collect();
for t in tutucular { t.join().unwrap(); }
oda.lock().unwrap().ayril("can");

Practice Room state

Write the Oda type above and share it with Arc<Mutex<Oda>>. Let ece, can and ali join from separate threads; once they're all done, let can leave. Sort the user list and print it with {:?}.

Starter code

main.rs
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::sync::{Arc, Mutex};
use std::thread;

struct Oda {
    uyeler: Vec<String>,
}

impl Oda {
    fn katil(&mut self, ad: &str) {
        self.uyeler.push(ad.to_string());
    }
    fn ayril(&mut self, ad: &str) {
        self.uyeler.retain(|k| k != ad);
    }
}

fn main() {
    let oda = Arc::new(Mutex::new(Oda { uyeler: Vec::new() }));
    let tutucular: Vec<_> = ["ece", "can", "ali"]
        .into_iter()
        .map(|ad| {
            let oda = Arc::clone(&oda);
            thread::spawn(move || oda.lock().unwrap().katil(ad))
        })
        .collect();
    for t in tutucular {
        t.join().unwrap();
    }
    oda.lock().unwrap().ayril("can");
    let mut liste = oda.lock().unwrap().uyeler.clone();
    liste.sort();
    println!("{:?}", liste);
}

Expected output

["ali", "ece"]

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Why is writing to a client over the network while holding the lock a bad idea?

  • A slow client holds the lock long and freezes the whole room Correct answer
  • The write fails
  • It doesn't compile
  • It isn't bad

Explanation: Under the lock, only take a copy of the list; do the I/O after releasing the lock.

2 Remove the leaver

Question
fn ayril(&mut self, ad: &str) {
    self.uyeler.____(|k| k != ad);
}
Answer
fn ayril(&mut self, ad: &str) {
    self.uyeler.retain(|k| k != ad);
}

Explanation: retain removes those that fail the condition.

3 Operation and room state

Term Meaning
ece, can, ali joined 3 users
can left 2 users
Sorted list ["ali", "ece"]
lock() held briefly The room doesn't freeze

All lessons in Advanced