Review

//api.rs
pub fn lock(&self) -> LockGuard<L, T> {
}

pub struct LockGuard<'s, L: RawLock, T> {
  lock: &'s Lock<L, T>,
  token: ManuallyDrop<L::Token>
}
// lifetime 's ensures that drop(g) should preceed drop(d)


impl<'s, L: RawLock, T> Deref for LockGuard<'s, L, T> {
  fn deref(&self) -> &self::Target {
    unsafe {
      self.lock.get_unchecked();
    }
  }
}

impl<'s, L: RawLock, T> DerefMut for LockGuard<'s, L, T> {
  fn deref_mut(&mut self) -> &mut self::Target {
    unsafe {
      self.lock.get_mut_unchecked();
    }
  }
}

g = d.lock();
// critical section
drop(g);

g = d.lock();
*g = 42;
bad = &mut *g;
drop(g);
*bad = 666; // g's lifetime ended!
drop(d);

deref taget shall not outlive guard

let d_guard = data.lock();
let d_ref = d_uard.deref();

drop(d_guard)
*d_ref // illegal

Safe API for lock based concurrency

Thread, channel, (rw) lock, mutex, etc.

use std::thread;
let thread_join_handle = thread::spanw(move || {
  todo!();
});

thread_join_handle.join(); wait until the thred ends

forking process vs spawning thread

  • similar concepts, but different internals; NOT COVERED IN CLASS

  • Allocating Thread Local STorage

id = tid();
addr = TLS[id]; // stored in the heap
// threads do not share this local storage 

spawn<F, T>(self, f: F)
where 
  F: FnOnce() -> T,
  F: Send + 'static, // closure called inside spawn should live long
  T: Send + 'static, //
  
//e.g.
let v = 42;
let r = &v;
spawn(|| {
  *r; // error: we don't know whether r is dangling or not: 
  // what if v is released?
})

why FnOnce?

let v = vec![1,2,3];
|| {
  v.into_iter().sum(); // consumes v: calling multiple times is not allowed
}
  • scoped thread: restricting thread's lifetime within a scope
spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
where 
  F: FnOnce() -> T + Send + 'scope,
  T: Send + 'scope, //

Anonymous functon is passed (wrapped) into a struct having the information of surroundig scope