Era of parallelism

  • end or Dennard scaling: no of transistors for fiexd area-core -> multi-core!
  • end of Moore’s law: no. of transistorf for fixed area -> hardward accelerator! ->how to coordinate parallel resources to achieve higher preformance? : synchronize safely and efficiently

  • concurrency: synchronizing parallel resources
  • parallelism: multiple resources
  • concurrency: Shared mutable resources
    • β€˜trivial’ concurrency: don’t share resources at any moment!
    • β€˜scalable’ concurrency: WHAT WE WANT! ->

Challenge in concurrency: nondeterminism(combinatorial!)

  • interleaving: β€˜x <- 1; x <- 2;’ depends on the order of execution

  • optimization by hardware / compiler : instruction reordering

    • mem[x] = 1; a = mem[y] || // compiler could read mem[y] first! mem[y] = 1; b = mem[x] -> (first and second instruction has no relationship)

Approaches to taming nondeterminism

  • APIs (enclosing with safe API)
  • Implementation (lock, condvar, data structure, synchronization patterns)

  • Easy: Lock-based concurrency
    • thread, lock, condvar
    • simple, but low scalability
  • Difficult: Lock-free concurrenc
    • semantics & reasoning principles (characterizing nondeterminism)
    • synchronization patters)
    • APIs

Shared-memory concurrency

  • thread: agent of execution (read/write shared memory)
  • shared memory: shared storage of data

Lock-based shared-memory concurrency

  • location is accessed by a single agent! - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚L.acquire() β”‚ β”‚L.acquire() β”‚ β”‚{ β”‚ β”‚{ β”‚ β”‚ r1 = mem[x]; β”‚ β”‚ r2 = mem[x]; β”‚ β”‚ mem[x] = r1 + 1; β”‚ β”‚ mem[x] = r2 + 1; β”‚ β”‚} β”‚ β”‚} β”‚ β”‚L.release() β”‚ β”‚L.release() β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  • apis
    • lock.acquire();
    • lock.try_acquire();
    • lock.release();
  • challenges:
    • relate lock and resource: users should access X only when L is held.
    • match acquire/relase: users should release only locks acquired.
  • c++/raii
    • LockGuard
      • lock.acquire() returns LockGuard
      • when LockGuard is destructed, the corresponding lock releases.
    • template<class T> class Lock<T> { RawLock lock; T data; }

    • WHAT’s WRONG?
      • raw pointer access -> data_ptr should not outlive data_guard
        auto data_guard = data.lock();
        auto data_ptr = (int*)&data_guard;
        //data_guard is dropped here
        foo(*data_ptr) // UAF!
        

Rust concurrency libraries

  • std::sync::Arc;
  • std::thread;
  • std::time::Duration;