Art of Multiprocessor Programming

Document Sample
Art of Multiprocessor Programming Powered By Docstoc
					   Mutual Exclusion




     Companion slides for
   The Art of Multiprocessor
         Programming
by Maurice Herlihy & Nir Shavit
        Mutual Exclusion


• We will clarify our understanding of
  mutual exclusion
• We will also show you how to reason
  about various properties in an
  asynchronous concurrent setting


               Art of Multiprocessor     2
                   Programming
          Mutual Exclusion
In his 1965 paper E. W. Dijkstra wrote:
"Given in this paper is a solution to a problem which,
 to the knowledge of the author, has been an open
 question since at least 1962, irrespective of the
 solvability. [...] Although the setting of the
 problem might seem somewhat academic at first,
 the author trusts that anyone familiar with the
 logical problems that arise in computer coupling
 will appreciate the significance of the fact that
 this problem indeed can be solved."
                    Art of Multiprocessor                3
                        Programming
          Mutual Exclusion


•   Formal problem definitions
•   Solutions for 2 threads
•   Solutions for n threads
•   Fair solutions
•   Inherent costs

                 Art of Multiprocessor   4
                     Programming
                  Warning
• You will never use these protocols
  – Get over it
• You are advised to understand them
  – The same issues show up everywhere
  – Except hidden and more complex




                  Art of Multiprocessor   5
                      Programming
       Why is Concurrent
     Programming so Hard?
• Try preparing a seven-course banquet
  – By yourself
  – With one friend
  – With twenty-seven friends …
• Before we can talk about programs
  – Need a language
  – Describing time and concurrency

                Art of Multiprocessor   6
                    Programming
                  Time
• “Absolute, true and mathematical
  time, of itself and from its own
  nature, flows equably without relation
  to anything external.” (I. Newton,
  1689)

• “Time is, like, Nature’s way of making
  sure that everything doesn’t happen
  all at once.” (Anonymous, circa 1968)

  time

               Art of Multiprocessor       7
                   Programming
                Events

• An event a0 of thread A is
  – Instantaneous
  – No simultaneous events (break ties)

         a0

 time


               Art of Multiprocessor      8
                   Programming
               Threads
• A thread A is (formally) a sequence
  a0, a1, ... of events
  – “Trace” model
  – Notation: a0  a1 indicates order

         a0   a1       a2        …

 time


                   Art of Multiprocessor   9
                       Programming
     Example Thread Events

•   Assign to shared variable
•   Assign to local variable
•   Invoke method
•   Return from method
•   Lots of other things …


                Art of Multiprocessor   10
                    Programming
Threads are State Machines
                                      a0
                        a3
Events are
transitions
                           a2              a1




              Art of Multiprocessor             11
                  Programming
                 States
• Thread State
  – Program counter
  – Local variables
• System state
  – Object fields (shared variables)
  – Union of thread states


                Art of Multiprocessor   12
                    Programming
         Concurrency

• Thread A
time




             Art of Multiprocessor   13
                 Programming
             Concurrency

• Thread A
time

• Thread B
        time



               Art of Multiprocessor   14
                   Programming
           Interleavings

• Events of two or more threads
  – Interleaved
  – Not necessarily independent (why?)

 time



                Art of Multiprocessor    15
                    Programming
              Intervals

• An interval A0 =(a0,a1) is
  – Time between events a0 and a1


        a0   A0       a1


 time

                  Art of Multiprocessor   16
                      Programming
  Intervals may Overlap


            b0     B0        b1


       a0    A0       a1


time

                  Art of Multiprocessor   17
                      Programming
Intervals may be Disjoint


                   b0        B0          b1


       a0   A0       a1


time

                 Art of Multiprocessor        18
                     Programming
            Precedence
Interval A0 precedes interval B0



                   b0        B0          b1

       a0   A0       a1


time

                 Art of Multiprocessor        19
                     Programming
            Precedence



• Notation: A0  B0
• Formally,
  – End event of A0 before start event of B0
  – Also called “happens before” or
    “precedes”
                Art of Multiprocessor     20
                    Programming
     Precedence Ordering



• Remark: A0  B0 is just like saying
  – 1066 AD  1492 AD,
  – Middle Ages  Renaissance,
• Oh wait,
  – what about this week vs this month?
                Art of Multiprocessor     21
                    Programming
       Precedence Ordering


•   Never true that A  A
•   If A B then not true that B A
•   If A B & B C then A C
•   Funny thing: A B & B A might both
    be false!
                Art of Multiprocessor     22
                    Programming
           Partial Orders
                      (review)


• Irreflexive:
  – Never true that A  A
• Antisymmetric:
  – If A  B then not true that B  A
• Transitive:
  – If A  B & B  C then A  C


                 Art of Multiprocessor   23
                     Programming
            Total Orders
                    (review)


• Also
  – Irreflexive
  – Antisymmetric
  – Transitive
• Except that for every distinct A, B,
  – Either A  B or B  A

                Art of Multiprocessor    24
                    Programming
          Repeated Events
while (mumble) {
  a0; a1;
}
                                  k-th occurrence
                                           of event a0

 a0   k                    k-th occurrence of
                            interval A0 =(a0,a1)
          A0   k
                   Art of Multiprocessor                 25
                       Programming
    Implementing a Counter

public class Counter {
  private long value;

    public long getAndIncrement() {
      temp = value;
      value = temp + 1;
      return temp;
    }                    Make these             steps
}                                  indivisible using
                Art of Multiprocessor    locks      26
                   Programming
     Locks (Mutual Exclusion)
public interface Lock {

    public void lock();

    public void unlock();
}




                 Art of Multiprocessor   27
                     Programming
     Locks (Mutual Exclusion)
public interface Lock {

    public void lock();                  acquire lock

    public void unlock();
}




                 Art of Multiprocessor                  28
                     Programming
     Locks (Mutual Exclusion)
public interface Lock {

    public void lock();                  acquire lock
    public void unlock();                release lock
}




                 Art of Multiprocessor                  29
                     Programming
              Using Locks
public class Counter {
  private long value;
  private Lock lock;
  public long getAndIncrement() {
   lock.lock();
   try {
    int temp = value;
    value = value + 1;
   } finally {
     lock.unlock();
   }
   return temp;
  }}

                  Art of Multiprocessor   30
                      Programming
             Using Locks
public class Counter {
  private long value;
  private Lock lock;
  public long getAndIncrement() {
   lock.lock();
   try {                      acquire    Lock
    int temp = value;
    value = value + 1;
   } finally {
     lock.unlock();
   }
   return temp;
  }}

                 Art of Multiprocessor          31
                     Programming
             Using Locks
public class Counter {
  private long value;
  private Lock lock;
  public long getAndIncrement() {
   lock.lock();
   try {
    int temp = value;
    value = value + 1;
   } finally {
     lock.unlock();           Release lock
   }                        (no matter what)
   return temp;
  }}

                 Art of Multiprocessor         32
                     Programming
              Using Locks
public class Counter {
  private long value;
  private Lock lock;
  public long getAndIncrement() {
   lock.lock();
   try {                                  Critical
    int temp = value;
    value = value + 1;                    section
   } finally {
     lock.unlock();
   }
   return temp;
  }}

                  Art of Multiprocessor              33
                      Programming
         Mutual Exclusion
• Let CSik   be thread i’s k-th critical
  section execution




               Art of Multiprocessor       34
                   Programming
        Mutual Exclusion
• Let CSik   be thread i’s k-th critical
  section execution
• And CSjm    be thread j’s m-th critical
  section execution




               Art of Multiprocessor   35
                   Programming
         Mutual Exclusion
• Let CSik   be thread i’s k-th critical
  section execution
• And CSjm    be j’s m-th execution
• Then either
  –       or




               Art of Multiprocessor       36
                   Programming
         Mutual Exclusion
• Let CSik   be thread i’s k-th critical
  section execution
• And CSjm    be j’s m-th execution
• Then either
  –       or


        CSik  CSjm

                Art of Multiprocessor      37
                    Programming
         Mutual Exclusion
• Let CSik   be thread i’s k-th critical
  section execution
• And CSjm    be j’s m-th execution
• Then either
  –       or


        CSik  CSjm
                                        CSjm  CSik
                Art of Multiprocessor                 38
                    Programming
          Deadlock-Free

• If some thread calls lock()
  – And never returns
  – Then other threads must complete lock()
    and unlock() calls infinitely often
• System as a whole makes progress
  – Even if individuals starve



                 Art of Multiprocessor    39
                     Programming
         Starvation-Free

• If some thread calls lock()
  – It will eventually return
• Individual threads make progress




                 Art of Multiprocessor   40
                     Programming
    Two-Thread vs n -Thread
          Solutions
• 2-thread solutions first
  – Illustrate most basic ideas
  – Fits on one slide
• Then n-thread solutions




                Art of Multiprocessor   41
                    Programming
   Two-Thread Conventions
class … implements Lock {
  …
  // thread-local index, 0 or 1
  public void lock() {
    int i = ThreadID.get();
    int j = 1 - i;
  …
  }
}



              Art of Multiprocessor   42
                  Programming
   Two-Thread Conventions
class … implements Lock {
  …
  // thread-local index, 0 or 1
  public void lock() {
    int i = ThreadID.get();
    int j = 1 - i;
  …
  }
}
           Henceforth: i is current
           thread, j is other thread
              Art of Multiprocessor    43
                  Programming
              LockOne
class LockOne implements Lock {
private boolean[] flag = new boolean[2];
public void lock() {
   flag[i] = true;
   while (flag[j]) {}
 }
              LockOne
class LockOne implements Lock {
private boolean[] flag = new boolean[2];
public void lock() {
   flag[i] = true;
   while (flag[j]) {}
 }                    Each thread has flag
              LockOne
class LockOne implements Lock {
private boolean[] flag = new boolean[2];
public void lock() {
   flag[i] = true;
   while (flag[j]) {}
                           Set my flag
 }
              LockOne
class LockOne implements Lock {
private boolean[] flag = new boolean[2];
public void lock() {
   flag[i] = true;
   while (flag[j]) {}
 }



             Wait for other flag to
                 become false
   LockOne Satisfies Mutual
          Exclusion
• Assume CSAj overlaps CSBk
• Consider each thread's last (j-th
  and k-th) read and write in the
  lock() method before entering
• Derive a contradiction


             Art of Multiprocessor    48
                 Programming
            From the Code

• writeA(flag[A]=true) 
  readA(flag[B]==false) CSA

• writeB(flag[B]=true) 
  readB(flag[A]==false)  CSB
      class LockOne implements Lock {
      …
      public void lock() {
          flag[i] = true;
          while (flag[j]) {}
        }
                 Art of Multiprocessor   49
                     Programming
      From the Assumption

• readA(flag[B]==false) 
  writeB(flag[B]=true)

• readB(flag[A]==false) 
  writeA(flag[A]=true)



              Art of Multiprocessor   50
                  Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            51
                      Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            52
                      Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            53
                      Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            54
                      Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            55
                      Programming
                Combining

• Assumptions:
  – readA(flag[B]==false)  writeB(flag[B]=true)
  – readB(flag[A]==false)  writeA(flag[A]=true)

• From the code
  – writeA(flag[A]=true)  readA(flag[B]==false)
  – writeB(flag[B]=true)  readB(flag[A]==false)




                  Art of Multiprocessor            56
                      Programming
  Cycle!




Art of Multiprocessor   57
    Programming
       Deadlock Freedom
• LockOne Fails deadlock-freedom
  – Concurrent execution can deadlock
  flag[i] = true;      flag[j] = true;
  while (flag[j]){}    while (flag[i]){}

  – Sequential executions OK




                 Art of Multiprocessor     58
                     Programming
                 LockTwo
public class LockTwo implements Lock {
 private int victim;
 public void lock() {
  victim = i;
  while (victim == i) {};
 }

 public void unlock() {}
}




                 Art of Multiprocessor   59
                     Programming
                 LockTwo
public class LockTwo implements Lock {
 private int victim;
 public void lock() {
                                Let other   go
  victim = i;                       first
  while (victim == i) {};
 }

 public void unlock() {}
}




                 Art of Multiprocessor           60
                     Programming
                 LockTwo
public class LockTwo implements Lock {
 private int victim;             Wait for
 public void lock() {           permission
 victim = i;
  while (victim == i) {};
 }

 public void unlock() {}
}




                 Art of Multiprocessor       61
                     Programming
                 LockTwo
public class Lock2 implements Lock {
 private int victim;
 public void lock() {         Nothing    to do
  victim = i;
  while (victim == i) {};
 }

 public void unlock() {}
}




                 Art of Multiprocessor           62
                     Programming
           LockTwo Claims

• Satisfies mutual exclusion
  – If thread i in CS      public void LockTwo() {
  – Then victim == j         victim = i;
                             while (victim == i) {};
  – Cannot be both 0 and 1
                                  }
• Not deadlock free
  – Sequential execution deadlocks
  – Concurrent execution does not


                   Art of Multiprocessor        63
                       Programming
     Peterson’s Algorithm

public void lock() {
  flag[i] = true;
  victim = i;
  while (flag[j] && victim == i) {};
}
public void unlock() {
  flag[i] = false;
}


             Art of Multiprocessor     64
                 Programming
     Peterson’s Algorithm
                                     Announce I’m
public void lock() {                  interested
  flag[i] = true;
  victim = i;
  while (flag[j] && victim == i) {};
}
public void unlock() {
  flag[i] = false;
}


             Art of Multiprocessor                  65
                 Programming
     Peterson’s Algorithm
                                     Announce I’m
public void lock() {                  interested
  flag[i] = true;
                          Defer to other
  victim = i;
  while (flag[j] && victim == i) {};
}
public void unlock() {
  flag[i] = false;
}


             Art of Multiprocessor                  66
                 Programming
     Peterson’s Algorithm
                                      Announce I’m
public void lock() {                   interested
  flag[i] = true;
                           Defer to other
  victim = i;
  while (flag[j] && victim == i) {};
}
public void unlock() {
  flag[i] = false;      Wait while other
}                       interested & I’m
                                       the victim

              Art of Multiprocessor                  67
                  Programming
     Peterson’s Algorithm
                                      Announce I’m
public void lock() {                   interested
  flag[i] = true;
                           Defer to other
  victim = i;
  while (flag[j] && victim == i) {};
  }
public void unlock() {
  flag[i] = false;      Wait while other
}                       interested & I’m
          No longer                    the victim
         interested
              Art of Multiprocessor                  68
                  Programming
         Mutual Exclusion

(1) writeB(Flag[B]=true)writeB(victim=B)
       public void lock() {
         flag[i] = true;
         victim = i;
         while (flag[j] && victim == i) {};
       }



                From the Code

                   Art of Multiprocessor      69
                       Programming
     Also from the Code

(2) writeA(victim=A)readA(flag[B])
    readA(victim)
     public void lock() {
       flag[i] = true;
       victim = i;
       while (flag[j] && victim == i) {};
     }




                Art of Multiprocessor       70
                    Programming
           Assumption

(3) writeB(victim=B)writeA(victim=A)

   W.L.O.G. assume A is the last
      thread to write victim




              Art of Multiprocessor     71
                  Programming
    Combining Observations

(1) writeB(flag[B]=true)writeB(victim=B)
(3) writeB(victim=B)writeA(victim=A)
(2) writeA(victim=A)readA(flag[B])
    readA(victim)
           Thus, A read flag[B] == true and
    victim == A, so it could not have
    entered the CS                     QED
                Art of Multiprocessor    72
                    Programming
            Deadlock Free
    public void lock() {
      …
      while (flag[j] && victim == i) {};


• Thread blocked
  – only at while loop
  – only if other’s flag is true
  – only if it is the victim
• Solo: other’s flag is false
• Both: one or the other not the victim

                    Art of Multiprocessor   73
                        Programming
            Starvation Free

• Thread i blocked
  only if j repeatedly       public void lock() {

  re-enters so that
                               flag[i] = true;
                               victim    = i;

  flag[j] == true and
                               while (flag[j] && victim == i) {};
                             }
  victim == i
                             public void unlock() {
• When j re-enters             flag[i] = false;
                             }
  – it sets victim to j.
  – So i gets in


                     Art of Multiprocessor                  74
                         Programming
 The Filter Algorithm for n
          Threads
There are n-1 “waiting rooms” called
  levels
                               ncs
• At each level
  – At least one enters level
  – At least one blocked if
    many try                            cs

• Only one thread makes it through
                Art of Multiprocessor        75
                    Programming
                    Filter
class Filter implements Lock {
   int[] level; // level[i] for thread i
   int[] victim; // victim[L] for level L
                                        2
    public Filter(int n) {          0             n-1
       level = new int[n];
                              level 0 0 4 0 0 0 0 0
       victim = new int[n];
       for (int i = 1; i < n; i++) {          1
           level[i] = 0;
       }}
     …                                     2 4

}
                Thread 2 at level 4              n-1
                  Art of Multiprocessor              76
                      Programming           victim
                  Filter
class Filter implements Lock {
  …

 public void lock(){
   for (int L = 1; L < n; L++) {
     level[i] = L;
     victim[L] = i;
      while (($ k != i level[k] >= L) &&
             victim[L] == i ) {};
   }}
 public void unlock() {
   level[i] = 0;
 }}


                Art of Multiprocessor      77
                    Programming
                  Filter
class Filter implements Lock {
  …

 public void lock() {
   for (int L = 1; L < n; L++) {
     level[i] = L;
     victim[L] = i;
      while (($ k != i) level[k] >= L) &&
             victim[L] == i) {};
   }}
 public void release(int i) {

                        One             level at a time
   level[i] = 0;
 }}


                Art of Multiprocessor               78
                    Programming
                  Filter
class Filter implements Lock {
  …

 public void lock() {
   for (int L = 1; L < n; L++) {
     level[i] = L;
     victim[L] = i;
      while (($ k != i) level[k] >= L) &&
                                          Announce
             victim[L] == i) {}; // busy wait
   }}
 public void release(int i) {           intention to
   level[i] = 0;                        enter level L
 }}


                Art of Multiprocessor                   79
                    Programming
                  Filter
class Filter implements Lock {
  int level[n];
  int victim[n];
  public void lock() {
    for (int L = 1; L < n; L++) {
      level[i] = L;
      victim[L] = i;
      while (($ k != i) level[k] >= L) &&
             victim[L] == i) {};
   }}
 public void release(int i) {           Give priority to
   level[i] = 0;                        anyone but me
 }}


                Art of Multiprocessor                 80
                    Programming
                 Filter
class Filter implements Lock {
  int as long
Wait level[n]; as someone else is at   same or
   higher level,
  int victim[n]; and I’m designated    victim
  public void lock() {
    for (int L = 1; L < n; L++) {
      level[i] = L;
      victim[L] = i;
     while (($ k != i) level[k] >= L) &&
            victim[L] == i) {};
   }}
 public void release(int i) {
   level[i] = 0;
 }}


               Art of Multiprocessor       81
                   Programming
                  Filter
class Filter implements Lock {
  int level[n];
  int victim[n];
  public void lock() {
    for (int L = 1; L < n; L++) {
      level[i] = L;
      victim[L] = i;
      while (($ k != i) level[k] >= L) &&
             victim[L] == i) {};
   }}
        enters level L i) {
Thread void=release(int when
 public
   level[i]  0;
                                        it completes
 }}              the loop

                Art of Multiprocessor              82
                    Programming
                 Claim
• Start at level L=0
• At most n-L threads enter level L
• Mutual exclusion at level L=n-1
                   ncs        L=0
                              L=1


                       L=n-2
                    cs L=n-1

               Art of Multiprocessor   83
                   Programming
      Induction Hypothesis
• No more than n-(L-1) at level L-1
• Induction step: by contradiction
• Assume all at level
  L-1 enter level L             ncs         assume
• A last to write
  victim[L]                              L-1 has n-(L-1)
                                         L has n-L
• B is any other
                                 cs
  thread at level L
                                           prove
                 Art of Multiprocessor                84
                     Programming
            Proof Structure
                ncs
                                   Assumed to enter L-1
            A     B
                             n-L+1 = 4
                              n-L+1 = 4
Last to
write           cs                 By way of contradiction
victim[L]
                                   all enter L
    Show that A must have seen
    B in level[L] and since victim[L] == A
    could not have entered
                      Art of Multiprocessor               85
                          Programming
         Just Like Peterson

(1) writeB(level[B]=L)writeB(victim[L]=B)
        public void lock() {
         for (int L = 1; L < n; L++) {
           level[i] = L;
           victim[L] = i;
          while (($ k != i) level[k] >= L)
                 && victim[L] == i) {};
          }}


                From the Code
                    Art of Multiprocessor    86
                        Programming
            From the Code

(2) writeA(victim[L]=A)readA(level[B])
                      readA(victim[L])

        public void lock() {
         for (int L = 1; L < n; L++) {
           level[i] = L;
           victim[L] = i;
          while (($ k != i) level[k] >= L)
                 && victim[L] == i) {};
          }}


                    Art of Multiprocessor    87
                        Programming
           By Assumption

(3) writeB(victim[L]=B)writeA(victim[L]=A)

      By assumption, A is the last
       thread to write victim[L]




                Art of Multiprocessor   88
                    Programming
    Combining Observations

(1) writeB(level[B]=L)writeB(victim[L]=B)
(3) writeB(victim[L]=B)writeA(victim[L]=A)
(2) writeA(victim[L]=A)readA(level[B])
        readA(victim[L])




                Art of Multiprocessor   89
                    Programming
    Combining Observations

(1) writeB(level[B]=L)writeB(victim[L]=B)
(3) writeB(victim[L]=B)writeA(victim[L]=A)
(2) writeA(victim[L]=A)readA(level[B])
    readA(victim[L])




                Art of Multiprocessor     90
                    Programming
    Combining Observations

(1) writeB(level[B]=L)writeB(victim[L]=B)
(3) writeB(victim[L]=B)writeA(victim[L]=A)
(2) writeA(victim[L]=A)readA(level[B])
     readA(victim[L])
               Thus, A read level[B] ≥ L,
   and victim[L] = A, so it could not have
   entered level L!
                 Art of Multiprocessor       91
                     Programming
           No Starvation
• Filter Lock satisfies properties:
  – Just like Peterson Alg at any level
  – So no one starves
• But what about fairness?
  – Threads can be overtaken by others




                 Art of Multiprocessor    92
                     Programming
        Bounded Waiting
• Want stronger fairness guarantees
• Thread not “overtaken” too much
• If A starts before B, then A enters
  before B?
• But what does “start” mean?
• Need to adjust definitions ….


              Art of Multiprocessor     93
                  Programming
         Bounded Waiting
• Divide lock() method into 2 parts:
  – Doorway interval:
    • Written DA
    • always finishes in finite steps
  – Waiting interval:
    • Written WA
    • may take unbounded steps



                  Art of Multiprocessor   94
                      Programming
  First-Come-First-Served
• For threads A and B:
  – If DAk  DB j
    • A’s k-th doorway precedes B’s j-th doorway
  – Then CSAk  CSBj
    • A’s k-th critical section precedes B’s j-th
      critical section
    • B cannot overtake A



                  Art of Multiprocessor             95
                      Programming
          Fairness Again
• Filter Lock satisfies properties:
  – No one starves
  – But very weak fairness
     • Can be overtaken arbitrary # of times
  – That’s pretty lame…




                Art of Multiprocessor     96
                    Programming
         Bakery Algorithm
• Provides First-Come-First-Served
• How?
  – Take a “number”
  – Wait until lower numbers have been
    served
• Lexicographic order
  – (a,i) > (b,j)
     • If a > b, or a = b and i > j

                    Art of Multiprocessor   97
                        Programming
      Bakery Algorithm
class Bakery implements Lock {
   boolean[] flag;
   Label[] label;
  public Bakery (int n) {
    flag = new boolean[n];
    label = new Label[n];
    for (int i = 0; i < n; i++) {
       flag[i] = false; label[i] = 0;
    }
  }
 …


               Art of Multiprocessor    98
                   Programming
      Bakery Algorithm
class Bakery implements Lock {
   boolean[] flag;
   Label[] label;                 2    6
                            0              n-1
  public Bakery (int n) {
    flag = new boolean[n]; f f t f f t f f
    label = new Label[n];    0 0 4 0 0 5 0 0
    for (int i = 0; i < n; i++) {
       flag[i] = false; label[i] = 0;
    }
  }
 …
                                      CS
              Art of Multiprocessor        99
                  Programming
         Bakery Algorithm
class Bakery   implements Lock {
  …
 public void   lock() {
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor         100
                      Programming
         Bakery Algorithm
class Bakery   implements Lock {
  …                                       Doorway
 public void   lock() {
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor             101
                      Programming
         Bakery Algorithm
class Bakery   implements Lock {
  …                                       I’m interested
 public void   lock() {
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor                102
                      Programming
         Bakery Algorithm
                                           Take increasing
                                          label (read labels
class Bakery   implements Lock {
  …                                       in some arbitrary
 public void   lock() {                         order)
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor               103
                      Programming
         Bakery Algorithm
class Bakery   implements Lock {          Someone is
  …                                       interested
 public void   lock() {
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor            104
                      Programming
         Bakery Algorithm
class Bakery implements Lock {
  boolean flag[n];
  int label[n];        Someone         is
                              interested
public void lock() {
 flag[i] = true;
 label[i] = max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}


                              With lower (label,i)
                             in lexicographic order
                  Art of Multiprocessor          105
                      Programming
       Bakery Algorithm
class Bakery implements Lock {

   …

 public void unlock() {
   flag[i] = false;
 }
}




                Art of Multiprocessor   106
                    Programming
       Bakery Algorithm
class Bakery implements Lock {
                                         No longer
   …                                    interested
 public void unlock() {
   flag[i] = false;
 }
}

       labels are always increasing


                Art of Multiprocessor                107
                    Programming
           No Deadlock
• There is always one thread with
  earliest label
• Ties are impossible (why?)




              Art of Multiprocessor   108
                  Programming
   First-Come-First-Served
• If DA  DBthen A’s          class Bakery implements Lock {

  label is smaller            public void lock() {
                                 flag[i] = true;
• And:                           label[i] = max(label[0],
                                                …,label[n-1])+1;
    writeA(label[A])           while ($k flag[k]
    readB(label[A])                      && (label[i],i) >
                                (label[k],k));
    writeB(label[B])          }
    readB(flag[A])
• So B sees smaller label
  for A is locked out
  while flag[A] is true
                   Art of Multiprocessor                  109
                       Programming
          Mutual Exclusion
• Suppose A and B in           class Bakery implements Lock {

  CS together                  public void lock() {
                                  flag[i] = true;
• Suppose A has                   label[i] = max(label[0],

  earlier label
                                                 …,label[n-1])+1;
                                 while ($k flag[k]
• When B entered, it                       && (label[i],i) >
                                 (label[k],k));
  must have seen                }

  – flag[A] is false, or
  – label[A] > label[B]

                    Art of Multiprocessor                  110
                        Programming
        Mutual Exclusion
• Labels are strictly increasing so
• B must have seen flag[A] == false




               Art of Multiprocessor   111
                   Programming
        Mutual Exclusion
• Labels are strictly increasing so
• B must have seen flag[A] == false
• LabelingB  readB(flag[A]) 
  writeA(flag[A])  LabelingA




               Art of Multiprocessor   112
                   Programming
        Mutual Exclusion
• Labels are strictly increasing so
• B must have seen flag[A] == false
• LabelingB  readB(flag[A]) 
  writeA(flag[A])  LabelingA
• Which contradicts the assumption
  that A has an earlier label


              Art of Multiprocessor   113
                  Programming
         Bakery Y232K Bug
class Bakery   implements Lock {
  …
 public void   lock() {
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor         114
                      Programming
         Bakery Y232K Bug
class Bakery   implements Lock {            Mutex breaks if
  …
 public void   lock() {
                                          label[i] overflows
  flag[i] =    true;
  label[i] =   max(label[0], …,label[n-1])+1;
    while ($k flag[k]
             && (label[i],i) > (label[k],k));
}




                  Art of Multiprocessor                115
                      Programming
       Does Overflow Actually
             Matter?
• Yes
  – Y2K
  – 18 January 2038 (Unix time_t rollover)
  – 16-bit counters
• No
  – 64-bit counters
• Maybe
  – 32-bit counters
                Art of Multiprocessor    116
                    Programming
            Timestamps
• Label variable is really a timestamp
• Need ability to
  – Read others’ timestamps
  – Compare them
  – Generate a later timestamp
• Can we do this without overflow?


               Art of Multiprocessor     117
                   Programming
            The Good News
• One can construct a
  –   Wait-free (no mutual exclusion)
  –   Concurrent
  –   Timestamping system
  –   That never overflows




                  Art of Multiprocessor   118
                      Programming
           The Good News
• One can construct a
  –   Wait-free (no mutual exclusion)
  –   Concurrent          This part       is hard
  –   Timestamping system
  –   That never overflows




                  Art of Multiprocessor       119
                      Programming
             Instead …
• We construct a Sequential
  timestamping system
  – Same basic idea
  – But simpler
• As if we use mutex to read & write
  atomically
• No good for building locks
  – But useful anyway

                Art of Multiprocessor   120
                    Programming
       Precedence Graphs


   0           1                    2   3
• Timestamps form directed graph
• Edge x to y
  – Means x is later timestamp
  – We say x dominates y
                Art of Multiprocessor       121
                    Programming
Unbounded Counter Precedence
           Graph


    0            1                    2   3
• Timestamping = move tokens on graph
• Atomically
  – read others’ tokens
  – move mine
• Ignore tie-breaking for now

                  Art of Multiprocessor       122
                      Programming
Unbounded Counter Precedence
           Graph


  0      1                    2   3




          Art of Multiprocessor       123
              Programming
Unbounded Counter Precedence
           Graph


  0         1                    2   3



takes 0   takes 1          takes 2

             Art of Multiprocessor       124
                 Programming
Two-Thread Bounded
 Precedence Graph

                0



    2                           1


        Art of Multiprocessor       125
            Programming
Two-Thread Bounded
 Precedence Graph

                0



    2                           1


        Art of Multiprocessor       126
            Programming
Two-Thread Bounded
 Precedence Graph

                0



    2                           1


        Art of Multiprocessor       127
            Programming
Two-Thread Bounded
 Precedence Graph

                0



    2                           1


        Art of Multiprocessor       128
            Programming
Two-Thread Bounded
Precedence Graph T2

                0

                                    and so on …


    2                           1


        Art of Multiprocessor                 129
            Programming
Three-Thread Bounded
 Precedence Graph?


     3                           0


     2                           1


         Art of Multiprocessor       130
             Programming
Three-Thread Bounded
 Precedence Graph?


     3                           0

                                     What to do if
     2                           1    one thread
                                      gets stuck?

         Art of Multiprocessor                131
             Programming
    Graph Composition
                                        0
    0

                                    2       1
2       1       T3=T2*T2
              Replace each vertex with a
                  copy of the graph

            Art of Multiprocessor               132
                Programming
Three-Thread Bounded
 Precedence Graph T3
                    0



              2
                   0       1


      0                               0



  2
      2   1                    2
                                   1      1




              Art of Multiprocessor           133
                  Programming
Three-Thread Bounded
 Precedence Graph T3
                    0
                                          20 < 02 < 12
              2
                   0       1


      0                               0



  2
      2   1                    2
                                   1      1




              Art of Multiprocessor                134
                  Programming
  Three-Thread Bounded
   Precedence Graph T3
                       0



                 2
                      0       1


         0                               0



     2
         2   1                    2
                                      1      1



and so on…
                 Art of Multiprocessor           135
                     Programming
                 In General
Tk = T2 * Tk-1                              label size =
                                            Log2(3k) =
K threads need 3k                           2k
      nodes




                    Art of Multiprocessor            136
                        Programming
Deep Philosophical Question
• The Bakery Algorithm is
  – Succinct,
  – Elegant, and
  – Fair.
• Q: So why isn’t it practical?
• A: Well, you have to read N distinct
  variables

                   Art of Multiprocessor   137
                       Programming
         Shared Memory
• Shared read/write memory locations
  called Registers (historical reasons)
• Come in different flavors
  – Multi-Reader-Single-Writer (Flag[])
  – Multi-Reader-Multi-Writer (Victim[])
  – Not that interesting: SRMW and SRSW



               Art of Multiprocessor   138
                   Programming
             Theorem
At least N MRSW (multi-
reader/single-writer) registers are
needed to solve deadlock-free
mutual exclusion.

N registers like Flag[]…




             Art of Multiprocessor    139
                 Programming
      Proving Algorithmic
         Impossibility                  C

•To show no algorithm exists:          write
  • assume by way of contradiction
     one does,
  • show a bad execution that            CS
     violates properties:
  • in our case assume an alg for deadlock
  free mutual exclusion using < N registers

              Art of Multiprocessor      140
                  Programming
Proof: Need N-MRSW Registers
Each thread must write to some register

      A             B                  C


                 write                write



     CS           CS                   CS

  …can’t tell whether A is in critical
                section
              Art of Multiprocessor           141
                  Programming
            Upper Bound
• Bakery algorithm
  – Uses 2N MRSW registers
• So the bound is (pretty) tight
• But what if we use MRMW registers?
  – Like victim[] ?




                 Art of Multiprocessor   142
                     Programming
    Bad News Theorem
At least N MRMW multi-
reader/multi-writer registers are
needed to solve deadlock-free
mutual exclusion.


 (So multiple writers don’t help)

              Art of Multiprocessor   143
                  Programming
 Theorem (For 2 Threads)
Theorem: Deadlock-free mutual
exclusion for 2 threads requires at
least 2 multi-reader multi-writer
registers

Proof: assume one register suffices
and derive a contradiction


             Art of Multiprocessor    144
                 Programming
     Two Thread Execution
            A                            B



                    Write(R)             R

            CS                       CS
• Threads run, reading and writing R
• Deadlock free so at least one gets in
                 Art of Multiprocessor       145
                     Programming
Covering State for One
Register Always Exists
                                   B




             Write(R)


In any protocol B has to write to
the register before entering CS,
     so stop it just before
           Art of Multiprocessor       146
               Programming
Proof: Assume Cover of 1

  A                             B



           Write(R)


       A runs, possibly writes to the
            register, enters CS
 CS
        Art of Multiprocessor       147
            Programming
Proof: Assume Cover of 1

  A                              B


                                B Runs, first
           Write(R)             obliterating
                                any trace of A,
                                then also enters
                                the critical
 CS            CS               section
        Art of Multiprocessor               148
            Programming
            Theorem

Deadlock-free mutual exclusion for 3
threads requires at least 3 multi-
reader multi-writer registers




             Art of Multiprocessor   149
                 Programming
Proof: Assume Cover of 2

  A               B                C



           Write(RB)            Write(RC)



                Only 2 registers

        Art of Multiprocessor               150
            Programming
      Run A Solo

A                 B                C



           Write(RB)            Write(RC)


   Writes to one or both
CS registers, enters CS
        Art of Multiprocessor               151
            Programming
Obliterate Traces of A

 A                 B                C



            Write(RB)            Write(RC)



       Other threads obliterate
 CS   evidence that A entered CS
         Art of Multiprocessor               152
             Programming
Mutual Exclusion Fails

A                B                 C



          Write(RB)            Write(RC)


                          CS looks empty, so
CS             CS          another thread
                                gets in
       Art of Multiprocessor               153
           Programming
         Proof Strategy
• Proved: a contradiction starting from
  a covering state for 2 registers
• Claim: a covering state for 2
  registers is reachable from any state
  where CS is empty



               Art of Multiprocessor   154
                   Programming
      Covering State for Two

              A                           B



           Write(RA)            Write(RB)


• If we run B through CS 3 times, B must
  return twice to cover some register, say RB
                  Art of Multiprocessor       155
                      Programming
       Covering State for Two

          A                   B



      Write(RA)        Write(RB)

• Start with B covering register RB for the 1st time
• Run A until it is about to write to uncovered RA
• Are we done?

                    Art of Multiprocessor              156
                        Programming
     Covering State for Two

       A                  B



    Write(RA)      Write(RB)

• NO! A could have written to RB
• So CS no longer looks empty to thread C

                Art of Multiprocessor   157
                    Programming
      Covering State for Two

         A                  B



     Write(RA)       Write(RB)

• Run B obliterating traces of A in RB
• Run B again until it is about to write to RB
• Now we are done
                  Art of Multiprocessor          158
                      Programming
    Inductively We Can Show
       A                B                  C



   Write(RA)     Write(RB)              Write(RC)

• There is a covering state
  – Where k threads not in CS cover k distinct
    registers
  – Proof follows when k = N-1
                Art of Multiprocessor               159
                    Programming
     Summary of Lecture
• In the 1960’s several incorrect
  solutions to starvation-free mutual
  exclusion using RW-registers were
  published…
• Today we know how to solve FIFO N
  thread mutual exclusion using 2N
  RW-Registers

              Art of Multiprocessor     160
                  Programming
     Summary of Lecture
• N RW-Registers inefficient
  – Because writes “cover” older writes
• Need stronger hardware operations
  – that do not have the “covering problem”
• In next lectures - understand what
  these operations are…


                Art of Multiprocessor     161
                    Programming
This work is licensed under a Creative Commons Attribution-
ShareAlike 2.5 License.
    • You are free:
        – to Share — to copy, distribute and transmit the work
        – to Remix — to adapt the work
    • Under the following conditions:
        – Attribution. You must attribute the work to ―The Art of
          Multiprocessor Programming‖ (but not in any way that
          suggests that the authors endorse you or your use of the
          work).
        – Share Alike. If you alter, transform, or build upon this work,
          you may distribute the resulting work only under the same,
          similar or a compatible license.
    • For any reuse or distribution, you must make clear to others the
      license terms of this work. The best way to do this is with a link
      to
        – http://creativecommons.org/licenses/by-sa/3.0/.
    • Any of the above conditions can be waived if you get permission
      from the copyright holder.
    • Nothing in this license impairs or restricts the author's moral
      rights.

                              Art of Multiprocessor                   162
                                  Programming