[OS] Synchronization 1

Updated:

Synchronization Background

  • Processes can execute concurrently
  • Concurrent access to shared data may result in data inconsistency (ex.multi-threads in a process OR multi-processes with shared memory)
  • Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes

sync

Synchronization Problem

  • Two or more concurrent threads(or processes) accessing and manipulating a shared resource create a race condition
  • Race condition may lead to non-deterministic results
  • OS is full of such a shared resource
  • The output of the program is not deterministic, it varies from run to run even with same inputs, depending on timing
  • Hard to debug… (“Heisenbugs…”)

Critical Section

  • A critical section is a piece of code that accesses a shared resource

critical_Section

  • Need mutual exclusion for correct execution with critical sections
    • Execute the critical section atomically (all or nothing)
    • Only one thread at a time can execute in the critical section
    • All other threads are forced to wait on entry
    • When a thread leaves a critical section, another can enter

Locks

  • An object (in memory) that provides mutual exclusion with the following two operations:
    • acquire() : wait until lock is free, then grab it
    • release() : unlock and wake up any thread waiting in acquire()
  • Using locks
    • Lock is initially free
    • Call acquire() before entering a critical section, and release() after leaving it
    • acquire() does not return until the caller holds the lock
    • On acquire(), a thread can spin(spinlock) or block(mutex)
    • At most one thread can hold a lock at a time

cri

Requirements for Locks

  • Correctness
    • Mutual exclusion : only one thread in critical section at a time
    • Progress : if several threads want to enter the same critical section, one must be allowed to proceed
    • Bounded waiting : starvation-free, must eventually allow each waiting thread to enter
  • Fairness
    • Each thread gets a fair chance at acquiring the lock
  • Performance
    • Time overhead for a lock

Initial implementation of Spinlock

spinlock

  • mutual exclusion problem!

Implementing Locks

  • Software-only algorithms
    • Dekker’s algorithm (1962)
    • Peterson’s algorithm (1981)
    • Lamport’s Bakery algorithm for more than two tasks (1974)
  • Hardware atomic instructions
    • Disable interrupts
    • Hardware atomic instructions

Second attempt to implement spinlock

spinlock2

  • progress problem!

Peterson’s algorithm

  • solves the critical section problem for two tasks

peterson

  • Mutual exclusion : Only one thread in critical section at a time
  • Progress : One will enter the critical section right after the other releases
  • Bounded waiting : Eventually allow each waiting thread to enter

Synchronization (Hardware support)

  • Peterson’s algorithm assumes load and store are atomic, but modern computer architectures are so complicated (some does not support atmoic)
  • Peterson’s algorithm only works in two-thread case
  • So we might be able to implement a correct locking mechanism based on hardware-provided atomicity

Disabling Interrupts

  • Preemption breaks the atomicity, thus we can prevent context switching by disabling interrupts
  • But, only kernel can enable/disable interrupts
  • Not practical in multiprocessor environments (take long to enable/disenable interrupts from all processors)

Atomic Instruction

  • Read-modify-write operations guaranteed to be executed “atomically”
  • Test-And-Set Instruction
    • returns the old value of a memory location while simultaneously updating it to the new value (ex. xchg in x86)

    atomic

Simple spinlock implementation using Test-And-Set instruction

simple_atomic

Compare-And-Swap (cmpxchg in x86)

  • Update the memory location with the new value only when its old value equals to the “expected” value
  • Return the old value

compare

Mutex Lock

  • Mut(ual) ex(clusive) lock : acquire() to lock, release() to unlock
  • busy-wait version (spinlock) or block version
  • usually implemented using the hardware-supported atomic instructions

Spinlock

  • A lock mechanism that keeps spinning until it acquires the lock
  • Disadvantage
    • Busy waiting burns system resources
    • If the lock holder is preempted …
  • Advantage
    • Simple to implement
    • Does not require a context switch (good for protecting short critical sections)

Semaphore

  • A synchronization primitive higher level than locks
  • Has an integer value S indicating its state
    • Determines the behavior of semaphore operations
    • Up to S tasks can grab the semaphore simultaneously
  • Operations
    • wait() : decrease S by one, and wait until S >= 0
    • signal() : increase S by one

Implementing Busy-waiting Semaphore

sema

  • wait() and signal() are critical sections, but ++ and – are not atomic

Implementing Blocking Semaphore

block

Leave a comment