; CENG334 Introduction to Operating Systems
Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

CENG334 Introduction to Operating Systems

VIEWS: 8 PAGES: 75

  • pg 1
									                            CENG334
                Introduction to Operating Systems

                       Synchronization primitives
                       Topics
                       •Spinlocks
                       •Strict Alternation

                       •Peterson’s solution

                       •Hardware solutions




                                       Erol Sahin

                               Dept of Computer Eng.
                           Middle East Technical University
                                  Ankara, TURKEY


URL: http://kovan.ceng.metu.edu.tr/~erol/Courses/CENG334

                                                              1
                                                         Spinlocks
  Very simple way to implement a lock:
                          struct lock {
                            int held = 0;
                          }
                          void acquire(lock) {
                             while (lock->held);                     The caller busy waits
                                                                     for the lock to be released
                             lock->held = 1;
                          }
                          void release(lock) {
                            lock->held = 0;
                          }




  Why doesn't this work?
           Where is the race condition?



Adapted from Matt Welsh’s (Harvard University) slides.                                             2
                                   Implementing Spinlocks
  Problem is that the internals of the lock acquire/release have critical
   sections too!
           The acquire( ) and release( ) actions must be atomic
           Atomic means that the code cannot be interrupted during execution
                “All or nothing” execution




                               struct lock {
                                 int held = 0;
                               }
                               void acquire(lock) {
                                  while (lock->held);           What can happen if there
                                  lock->held = 1;               is a context switch here?
                               }
                               void release(lock) {
                                 lock->held = 0;
                               }




Adapted from Matt Welsh’s (Harvard University) slides.                                      3
                                   Implementing Spinlocks
  Problem is that the internals of the lock acquire/release have critical
   sections too!
           The acquire( ) and release( ) actions must be atomic
           Atomic means that the code cannot be interrupted during execution
                “All or nothing” execution




                               struct lock {
                                 int held = 0;
                               }
                               void acquire(lock) {
                                  while (lock->held);             This sequence needs
                                  lock->held = 1;                 to be atomic
                               }
                               void release(lock) {
                                 lock->held = 0;
                               }




Adapted from Matt Welsh’s (Harvard University) slides.                                  4
                                   Implementing Spinlocks
  Problem is that the internals of the lock acquire/release have critical
   sections too!
           The acquire( ) and release( ) actions must be atomic
           Atomic means that the code cannot be interrupted during execution
                “All or nothing” execution




  Doing this with help from hardware!
           Disabling interrupts
                Why does this prevent a context switch from occurring?


           Atomic instructions – CPU guarantees entire action will execute atomically
                Test-and-set


                Compare-and-swap




  Doing this in only software!
           Strict alternation
                 Not an acceptable solution


           Peterson’s solution



Adapted from Matt Welsh’s (Harvard University) slides.                                   5
                             Spinlocks using test-and-set
  CPU provides the following as one atomic instruction:

                                 bool test_and_set(bool *flag) {
                                   … // Hardware dependent implementation
                                 }




  So to fix our broken spinlocks, we do this:
                              struct lock {
                                int held = 0;
                              }
                              void acquire(lock) {
                                 while(test_and_set(&lock->held));
                              }
                              void release(lock) {
                                lock->held = 0;
                              }

Adapted from Matt Welsh’s (Harvard University) slides.                      6
                           What's wrong with spinlocks?

  OK, so spinlocks work (if you implement them correctly), and
   they are simple. So what's the catch?

                           struct lock {
                             int held = 0;
                           }
                           void acquire(lock) {
                              while(test_and_set(&lock->held));
                           }
                           void release(lock) {
                             lock->held = 0;
                           }




Adapted from Matt Welsh’s (Harvard University) slides.            7
                                   Problems with spinlocks
  Horribly wasteful!
           Threads waiting to acquire locks spin on the CPU
           Eats up lots of cycles, slows down progress of other threads
                Note that other threads can still run ... how?


           What happens if you have a lot of threads trying to acquire the lock?


  Only want spinlocks as primitives to build higher-level synchronization
   constructs




Adapted from Matt Welsh’s (Harvard University) slides.                              8
                                         Disabling Interrupts
  An alternative to spinlocks:
                         struct lock {
                           // Note – no state!
                         }
                         void acquire(lock) {
                            cli();   // disable interrupts
                         }
                         void release(lock) {
                            sti();   // reenable interupts
                         }



           Can two threads disable/reenable interrupts at the same time?


  What's wrong with this approach?



Adapted from Matt Welsh’s (Harvard University) slides.                      9
                                         Disabling Interrupts
  An alternative to spinlocks:
                         struct lock {
                           // Note – no state!
                         }
                         void acquire(lock) {
                            cli();   // disable interrupts
                         }
                         void release(lock) {
                            sti();   // reenable interupts
                         }


           Can two threads disable/reenable interrupts at the same time?


  What's wrong with this approach?
           Can only be implemented at kernel level (why?)
           Inefficient on a multiprocessor system (why?)
           All locks in the system are mutually exclusive
                 No separation between different locks for different bank accounts



Adapted from Matt Welsh’s (Harvard University) slides.                                10
        Software solution 1: Strict Alternation
int turn = 0;/*shared variable*/

void *thread1(){
while(1){
    while (turn != 0) ;/*busy wait*/
    critical_region();
    turn = 1;
    noncritical_region();
}
}

void *thread2(){                          Guarantees mutual exclusion.
while(1){
    while (turn != 1) ;/*busy wait*/      Does not guarantee progress ---
                                           enforces strict alternation of
    critical_region();                     processes entering CS's.
    turn = 0;
    noncritical_region();                 Bounded waiting violated --- suppose
}                                          one process terminates while its its
                                           turn?
}



                                                                              11
        Software Solution 2: Peterson’s solution
#define FALSE 0
#define TRUE 1
#define N 2
int turn;/*shared variable*/
int interested[N]; /* init to all 0’s */


void enter_region(int process){
int other;    /* the number of the other process */
    other = 1-process;
    interested[process] = TRUE;
    turn = process;
    while(turn==process && interested[other]==TRUE);
}


void leave_region(int process){
    interested[process] =    FALSE;
}




                                                       12
                            Peterson’s solution
/* Initially neither process is in the critical region. */
#define FALSE 0
#define TRUE 1
#define N 2
int turn;/*shared variable*/
int interested[N]; /* init to all 0’s */


void enter_region(int process){ /* first called by process   0 */
int other;    /* the number of the other process */
    other = 1-process;
    interested[process] = TRUE; /* interested[0] = TRUE interested[1]= FALSE*/
    turn = process;               /* turn = 0 */
    while(turn==process && interested[other]==TRUE); /* returns immediately */
}


void leave_region(int process){
    interested[process] =    FALSE;
}




                                                                                 13
                            Peterson’s solution
/* Process 1 calls enter_region while process 0 is still in */
#define FALSE 0
#define TRUE 1
#define N 2
int turn;/*shared variable*/
int interested[N]; /* init to all 0’s */


void enter_region(int process){ /* called by process   1 */
int other;    /* the number of the other process */
    other = 1-process;
    interested[process] = TRUE; /* interested[0] = TRUE interested[1] = TRUE*/
    turn = process;               /* turn = 1 */
    while(turn==process && interested[other]==TRUE); /* blocks on busy wait */
}         /* until process 0 calls leave_region() to set interested[0] = 0 */


void leave_region(int process){
    interested[process] =    FALSE;
}




                                                                                 14
                            Peterson’s solution
/* Initially neither process is in the critical region. */
#define FALSE 0
#define TRUE 1
#define N 2
int turn;/*shared variable*/
int interested[N]; /* init to all 0’s */


void enter_region(int process){ /* first called by process 0 and process 1 */
int other;    /* the number of the other process */
other = 1-process;
    interested[process] = TRUE; /* interested[0] = TRUE interested[1]= TRUE */
    turn = process;               /* race condition: turn = 0 or turn = 1     */
    while(turn==process && interested[other]==TRUE); /* returns immediately */
}             /* for the process who last set turn. The other remains waiting */


void leave_region(int process){
    interested[process] =    FALSE;
}




                                                                                   15
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                  ???
                                                         1) Check lock state

                                     Thread 1




                  Lock state         unlocked


            Lock wait queue
                                               Ø

Adapted from Matt Welsh’s (Harvard University) slides.                         16
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                         1) Check lock state

                                     Thread 1
                                                         2) Set state to locked


                                                         3) Enter critical section

                  Lock state           locked


            Lock wait queue
                                               Ø

Adapted from Matt Welsh’s (Harvard University) slides.                               17
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                            ???
                                                         1) Check lock state

                Thread 2             Thread 1




                  Lock state           locked


            Lock wait queue
                                               Ø

Adapted from Matt Welsh’s (Harvard University) slides.                         18
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                         1) Check lock state

                Thread 2             Thread 1
                                                         2) Add self to wait queue (sleep)




                  Lock state           locked


            Lock wait queue
                                               Ø
                                           Thread 2



Adapted from Matt Welsh’s (Harvard University) slides.                                       19
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                            ???
                                                            1) Check lock state

                Thread 3             Thread 1
                                                            2) Add self to wait queue (sleep)




                  Lock state           locked


            Lock wait queue

                                           Thread 2      Thread 3



Adapted from Matt Welsh’s (Harvard University) slides.                                          20
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                            1) Thread 1 finishes critical section

                                     Thread 1




                  Lock state           locked


            Lock wait queue

                                           Thread 2      Thread 3



Adapted from Matt Welsh’s (Harvard University) slides.                                              21
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                            1) Thread 1 finishes critical section

                Thread 3             Thread 1
                                                            2) Reset lock state to unlocked

                                                            3) Wake one thread from wait queue

                  Lock state         unlocked


            Lock wait queue

                                           Thread 2      Thread 3



Adapted from Matt Welsh’s (Harvard University) slides.                                              22
                                Mutexes – Blocking Locks
  Really want a thread waiting to enter a critical section to block
           Put the thread to sleep until it can enter the critical section
           Frees up the CPU for other threads to run


  Straightforward to implement using our TCB queues!

                                                         Thread 3 can now grab lock and
                                                         enter critical section
                                     Thread 3




                  Lock state           locked


            Lock wait queue

                                           Thread 2



Adapted from Matt Welsh’s (Harvard University) slides.                                    23
                                         Limitations of locks
  Locks are great, and simple. What can they not easily accomplish?

  What if you have a data structure where it's OK for many threads
   to read the data, but only one thread to write the data?
           Bank account example.
           Locks only let one thread access the data structure at a time.




Adapted from Matt Welsh’s (Harvard University) slides.                       24
                                         Limitations of locks
  Locks are great, and simple. What can they not easily accomplish?

  What if you have a data structure where it's OK for many threads
   to read the data, but only one thread to write the data?
           Bank account example.
           Locks only let one thread access the data structure at a time.


  What if you want to protect access to two (or more)
   data structures at a time?
           e.g., Transferring money from one bank account to another.
           Simple approach: Use a separate lock for each.
           What happens if you have transfer from account A -> account B, at the same time
            as transfer from account B -> account A?
                Hmmmmm ... tricky.




Adapted from Matt Welsh’s (Harvard University) slides.                                        25
                                 CENG334
                     Introduction to Operating Systems


                        Semaphores
                        Topics:
                        •Need for higher-level synchronization primitives

                        •Semaphores and their implementation

                        •The Producer/Consumer problem and its solution with

                        semaphores
                        •The Reader/Writer problem and its solution with semaphores




                                        Erol Sahin

                               Dept of Computer Eng.
                           Middle East Technical University
                                  Ankara, TURKEY


URL: http://kovan.ceng.metu.edu.tr/ceng334

                                                                                      26
         Higher-level synchronization primitives
We have looked at one synchronization primitive: locks

Locks are useful for many things, but sometimes programs have
 different requirements.

Examples?
        Say we had a shared variable where we wanted any number of threads to read
         the variable, but only one thread to write it.
        How would you do this with locks?

          Reader() {                                      Writer() {
            lock.acquire();                                 lock.acquire();
            mycopy = shared_var;                            shared_var = NEW_VALUE;
            lock.release();                                 lock.release();
            return mycopy;                                }
          }


What's wrong with this code?


 Adapted from Matt Welsh’s (Harvard University) slides.                               27
                                          Semaphores
Higher-level synchronization
 construct                                                          Semaphore
        Designed by Edsger Dijkstra in the
         1960's, part of the THE operating
         system (classic stuff!)

Semaphore is a shared counter

Two operations on semaphores:

P() or wait() or down()
        From Dutch “proeberen”, meaning “test”
        Atomic action:
             Wait for semaphore value to become > 0, then decrement it




V() or signal() or up()
        From Dutch “verhogen”, meaning “increment”
        Atomic action:
             Increments semaphore value by 1.




 Adapted from Matt Welsh’s (Harvard University) slides.                         28
                                Semaphore Example
Semaphores can be used to implement locks:
       Semaphore my_semaphore = 1; // Initialize to nonzero

       int withdraw(account, amount) {
           down(my_semaphore);
           balance = get_balance(account);
           balance -= amount;                             critical
                                                          section
           put_balance(account, balance);
           up(my_semaphore);
           return balance;
       }



A semaphore where the counter value is only 0 or 1 is
 called a binary semaphore.



 Adapted from Matt Welsh’s (Harvard University) slides.              29
            Simple Semaphore Implementation
struct semaphore {
   int val;
   threadlist L; // List of threads waiting for semaphore
}

down(semaphore S):     // Wait until > 0 then decrement
    while (S.val <= 0) {                        Why is this a while loop
        add this thread to S.L;                 and not just an if statement?
        block(this thread);
    }
    S.val = S.val -1;
    return;

up(semaphore S):      // Increment value and wake up next thread
   S.val = S.val + 1;
   if (S.L is nonempty) {
       remove a thread T from S.L;
       wakeup(T);
   }


  What's wrong with this picture???

Adapted from Matt Welsh’s (Harvard University) slides.                      30
            Simple Semaphore Implementation
struct semaphore {
   int val;
   threadlist L; // List of threads waiting for semaphore
}

down(semaphore S):    // Wait until > 0 then decrement
    while (S.val <= 0) {
         add this thread to S.L;
         block(this thread);
    }
    S.val = S.val -1;
    return;                                                        down() and
                                                                  up() must be
up(semaphore S):     // Increment value and wake up next thread      atomic
   S.val = S.val + 1;
   if (S.L is nonempty) {                                           actions!
     remove a thread T from S.L;
     wakeup(T);
   }




Adapted from Matt Welsh’s (Harvard University) slides.                           31
                        Semaphore Implementation
How do we ensure that the semaphore implementation is atomic?




  Adapted from Matt Welsh’s (Harvard University) slides.        32
                        Semaphore Implementation
How do we ensure that the semaphore implementation is atomic?

One approach: Make them system calls, and ensure only one
 down() or up() operation can be executed by any process at a
 time.
       This effectively puts a lock around the down() and up() operations themselves!
       Easy to do by disabling interrupts in the down() and up() calls.

Another approach: Use hardware support
       Say your CPU had atomic down and up instructions




  Adapted from Matt Welsh’s (Harvard University) slides.                                 33
         OK, but why are semaphores useful?
A binary semaphore (counter is always 0 or 1) is basically a
 lock.

The real value of semaphores becomes apparent when the
 counter can be initialized to a value other than 0 or 1.

Say we initialize a semaphore's counter to 50.
        What does this mean about down() and up() operations?




Adapted from Matt Welsh’s (Harvard University) slides.           34
                The Producer/Consumer Problem
 Also called the Bounded Buffer problem.
                                                               Mmmm... donuts




Producer                                                   Consumer




 Producer pushes items into the buffer.

 Consumer pulls items from the buffer.

 Producer needs to wait when buffer is full.

 Consumer needs to wait when the buffer is empty.




  Adapted from Matt Welsh’s (Harvard University) slides.                        35
                The Producer/Consumer Problem
 Also called the Bounded Buffer problem.
                                                                  zzzzz....


Producer                                                   Consumer




 Producer pushes items into the buffer.

 Consumer pulls items from the buffer.

 Producer needs to wait when buffer is full.

 Consumer needs to wait when the buffer is empty.




  Adapted from Matt Welsh’s (Harvard University) slides.                  36
                               One implementation...

Producer                                                                      Consumer



int count = 0;

Producer() {                                               Consumer() {
int item;                                                  int item;
while (TRUE) {                                             while (TRUE) {
   item = bake();                                             if (count == 0) sleep();
   if (count == N) sleep();                                   item = remove_item();
   insert_item(item);                                         count = count – 1;
   count = count + 1;                                         if (count == N-1)
   if (count == 1)                                                wakeup(producer);
       wakeup(consumer);                                      eat(item);
}                                                          }
}                                                          }

 What's wrong with this code?                                What if we context switch right here??


  Adapted from Matt Welsh’s (Harvard University) slides.                                              37
                             A fix using semaphores

Producer                                                                   Consumer



Semaphore mutex = 1;
Semaphore empty = N;
Semaphore full = 0;
                                                           Consumer() {
Producer() {
                                                           int item;
int item;
                                                           while (TRUE) {
while (TRUE) {
                                                              down(full);
   item = bake();
                                                              down(mutex);
   down(empty);
                                                              item = remove_item();
   down(mutex);
                                                              up(mutex);
   insert_item(item);
                                                              up(empty);
   up(mutex);
                                                              eat(item);
   up(full);
                                                           }
}
                                                           }
}



  Adapted from Matt Welsh’s (Harvard University) slides.                              38
                          Issues with Semaphores
Much of the power of semaphores derives from calls to
 down() and up() that are unmatched
         See previous example!

Unlike locks, acquire() and release() are not always paired.

This means it is a lot easier to get into trouble with semaphores.
         “More rope”

Would be nice if we had some clean, well-defined language
 support for synchronization...
         Java does!




Adapted from Matt Welsh’s (Harvard University) slides.               40
                                 CENG334
                     Introduction to Operating Systems


                        Monitors, Condition variabless
                        Topics:
                        •Monitors

                        •Condition Variables




                                         Erol Sahin

                               Dept of Computer Eng.
                           Middle East Technical University
                                  Ankara, TURKEY


URL: http://kovan.ceng.metu.edu.tr/ceng334

                                                              41
                    Readers/Writers Problem
                              W

                                                             R
                                R
                                                         R


Motivation: Consider a shared database
      Two classes of users:
            Readers – never modify database


            Writers – read and modify database

      Is using a single lock on the whole database sufficient?
            Like to have many readers at the same time


            Only one writer at a time




                                                                  42
                                  Reader/Writers Problem
Single shared object

Want to allow any number of threads to read simultaneously

But, only one thread should be able to write to the object at a time
         (And, not interfere with any readers...)


Conditions:
    A Reader should only wait for a Writer to complete its do_write().


    A Reader should not wait for other Readers to complete their do_read().


    The Writer should wait for the other Writers to complete their do_write().


    The Writer should wait for all the Readers to complete their do_read().




Basic structure of a solution
  
         Reader()
              Wait until no writers
              Access data base
              Check out – wake up a waiting writer
         Writer()
              Wait until no active readers or writers
              Access database
              Check out – wake up waiting readers or writer


      Adapted from Matt Welsh’s (Harvard University) slides.                      43
                  Readers-Writers Problem
One issue we need to settle, to clarify problem statement.
      Suppose that a writer is active and a mixture of readers and writers now shows
       up. Who should get in next?
      Or suppose that a writer is waiting and an endless of stream of readers keeps
       showing up. Is it fair for them to become active?


We’ll favor a kind of back-and-forth form of fairness:
      Once a reader is waiting, readers will get in next.
      If a writer is waiting, one writer will get in next.




                                                                                        44
                                       Reader/Writers

                                                                       Why the test
        Semaphore mutex = 1;                             Reader() {    here??
        Semaphore wrt = 1;                                  down(mutex);
        int readcount = 0;                                  readcount++;
                                                            if (readcount == 1) {
        Writer() {                                              down(wrt);
           down(wrt);                                       }
           do_write();                                      up(mutex);
           up(wrt);                                         do_read();
        }                                                   down(mutex);
                                                            readcount--;
                                                            if (readcount == 0) {
                                                                up(wrt);
                                                            }
                                                            up(mutex);
                                                         }




Adapted from Matt Welsh’s (Harvard University) slides.                                45
                          Issues with Semaphores
Much of the power of semaphores derives from calls to
 down() and up() that are unmatched
         See previous example!

Unlike locks, acquire() and release() are not always paired.

This means it is a lot easier to get into trouble with semaphores.
         “More rope”

Would be nice if we had some clean, well-defined language
 support for synchronization...
         Java does!




Adapted from Matt Welsh’s (Harvard University) slides.               46
                         Monitors
 A high-level abstraction that provides a convenient and
  effective mechanism for process synchronization
 Only one process may be active within the monitor at a time




                                                                47
               Monitor with Condition Variables
   condition x, y;

   Two operations on a condition
    variable:
        x.wait () – a process that
         invokes the operation is
         suspended.
        x.signal () – resumes one
         of processes (if any) that
         invoked x.wait ()




                                                  48
                   Java Synchronization Support
Java synchronization based on locks and condition variables
       Every Java object can be used as a lock
                   Object foo;
                   synchronized (foo) {
                     // Do some stuff with 'foo' locked...
                   }


What's nice about this?
       Compiler ensures that lock is released before leaving the synchronized block
           Even if there is an exception!!




Condition variable (CV): allows a thread to wait or wakeup other
 threads

Three operations on condition variables:
       wait() -- Block until another thread calls notify() or notifyAll() on the CV
       notify() or signal -- Wake up one thread waiting on the CV
       notifyAll() -- Wake up all threads waiting on the CV


Adapted from Matt Welsh’s (Harvard University) slides.                                 49
                        Bounded Buffer using CV's
int theArray[ARRAY_SIZE], size;     int get() {
Object theLock;                        int item;
                                       synchronized(theLock) {
void put(int val) {                        while (size == 0) {
   synchronized(theLock) {                    theLock.wait();
       while (size == ARRAY_SIZE) {        }
         theLock.wait();                   item = getItemFromArray()
       }                                   size--;
       addItemToArray(val);                if (size == ARRAY_SIZE-1)
       size++;                                theLock.notify();
       if (size == 1)                  }
           theLock.notify();           return item;
   }
}                                   }


 Problems with this code??



 Adapted from Matt Welsh’s (Harvard University) slides.            50
                        Bounded Buffer using CV's
int theArray[ARRAY_SIZE], size;     int get() {
Object theLock;                        int item;
                                       synchronized(theLock) {
void put(int val) {                        while (size == 0) {
   synchronized(theLock) {                    theLock.wait();
       while (size == ARRAY_SIZE) {        }
         theLock.wait();                   item = getItemFromArray()
       }                                   size--;
       addItemToArray(val);                if (size == ARRAY_SIZE-1)
       size++;                                theLock.notify();
       if (size == 1)                  }
           theLock.notify();           return item;
   }
}                                   }


 Assumes only a single thread calling put() and get() at a time!
 If two threads call get(), then two threads call put(), only one
 will be woken up!!


 Adapted from Matt Welsh’s (Harvard University) slides.             51
                           How to fix this problem?
int theArray[ARRAY_SIZE], size;     int get() {
Object theLock;                        int item;
                                       synchronized(theLock) {
void put(int val) {                        while (size == 0) {
   synchronized(theLock) {                    theLock.wait();
       while (size == ARRAY_SIZE) {        }
         theLock.wait();                   item = getItemFromArray()
       }                                   size--;
       addItemToArray(val);                if (size == ARRAY_SIZE-1)
       size++;                                theLock.notifyAll();
       if (size == 1)                  }
           theLock.notifyAll();        return item;
   }
}                                   }
Every notifyAll() will cause all threads to wake up and re-check the
 empty or full condition.
        Could be inefficient if a lot of threads are involved...



 Adapted from Matt Welsh’s (Harvard University) slides.                52
                                                  Monitors
This style of using locks and CV's to protect access to a shared
 object is often called a monitor
      Think of a monitor as a lock protecting an object, plus a queue of waiting threads.



                                                             Shared data

                      Waiting threads




                    At most one thread                      Methods accessing
                    in the monitor at a                        shared data
                    time



                        How is this different than a lock???

   Adapted from Matt Welsh’s (Harvard University) slides.                                    53
                                               Monitors
                                                          unlocked

                                                          Shared data




                                                         Methods accessing
                                                            shared data




Adapted from Matt Welsh’s (Harvard University) slides.                       54
                                               Monitors
                                                                locked

                                                              Shared data



                                                   zzzz...




                                                             Methods accessing
                          zzzz...                               shared data




       Sleeping thread no longer “in” the monitor.
       (But not on the waiting queue either! Why?)




Adapted from Matt Welsh’s (Harvard University) slides.                           55
                                               Monitors
               Monitor stays locked!                             locked
               (Lock now owned by
               different thread...)
                                                               Shared data




                                                   notify()
                                                              Methods accessing
                         zzzz...                                 shared data




Adapted from Matt Welsh’s (Harvard University) slides.                            56
                                               Monitors
                                                                 locked

                                                               Shared data




                                                   notify()
                                                              Methods accessing
                                                                 shared data




Adapted from Matt Welsh’s (Harvard University) slides.                            57
                                               Monitors
                                                            locked

                                                          Shared data




                                                         Methods accessing
                                                            shared data




       No guarantee which order threads get into the monitor.
       (Not necessarily FIFO!)




Adapted from Matt Welsh’s (Harvard University) slides.                       58
                                              CVs in Java
In Java, a thread can only call wait(), notify(), or notifyAll() if it
  holds the lock of the associated object

                      Object foo;

                      /* NOT ALLOWED!!! */
                      foo.notify();

                      /* This is ok ... */
                      synchronized(foo) { foo.notify(); }

Why?




   Adapted from Matt Welsh’s (Harvard University) slides.                59
                                            CVs in Java
In Java, a thread can only call wait(), notify(), or notifyAll() if it
  holds the lock of the associated object
                    Object foo;

                    /* NOT ALLOWED!!! */
                    foo.notify();

                    /* This is ok ... */
                    synchronized(foo) { foo.notify(); }


Why?
        Want to avoid common race conditions!
         if (foo.count < 0) {                             /* Another thread... */
         synchronized (foo) {                             synchronized (foo) {
            foo.wait();                                   foo.count++;
         }                                                foo.notify();
         }                                                }




 Adapted from Matt Welsh’s (Harvard University) slides.                             60
             Hoare vs. Mesa Monitor Semantics
The monitor notify() operation can have two different meanings:

Hoare monitors (1974)
       notify(CV) means to run the waiting thread immediately
       Causes notifying thread to block

Mesa monitors (Xerox PARC, 1980)
       notify(CV) puts waiting thread back onto the “ready queue” for the monitor
       But, notifying thread keeps running




Adapted from Matt Welsh’s (Harvard University) slides.                               61
             Hoare vs. Mesa Monitor Semantics
The monitor notify() operation can have two different meanings:

Hoare monitors (1974)
       notify(CV) means to run the waiting thread immediately
       Causes notifying thread to block

Mesa monitors (Xerox PARC, 1980)
       notify(CV) puts waiting thread back onto the “ready queue” for the monitor
       But, notifying thread keeps running

What's the practical difference?
       In Hoare-style semantics, the “condition” that triggered the notify()
        will always be true when the awoken thread runs
             For example, that the buffer is now no longer empty


       In Mesa-style semantics, awoken thread has to recheck the condition
             Since another thread might have beaten it to the punch




Adapted from Matt Welsh’s (Harvard University) slides.                               62
                            Hoare vs. Mesa monitors
Need to be careful about precise definition of signal and wait.
         while (n==0) {
             wait(not_empty); // If nothing, sleep
         }
         item = getItemFromArray();          // Get next item
      Why didn’t we do this?

          if (n==0) {
             wait(not_empty); // If nothing, sleep
          }
          removeItemFromArray(val); // Get next item

Answer: depends on the type of scheduling
      Hoare-style (most textbooks):
          Signaler gives lock, CPU to waiter; waiter runs immediately

          Waiter gives up lock, processor back to signaler when it exits critical section or if it

           waits again
      Mesa-style (Java, most real operating systems):
          Signaler keeps lock and processor

          Waiter placed on ready queue with no special priority

          Practically, need to check condition again after wait




                                                                                                      63
               Revisit: Readers/Writers Problem
Correctness Constraints:
      Readers can access database when no writers
      Writers can access database when no readers or writers
      Only one thread manipulates state variables at a time

State variables (Protected by a lock called “lock”):
             int NReaders: Number of active readers; initially = 0
             int WaitingReaders: Number of waiting readers; initially = 0
             int NWriters: Number of active writers; initially = 0
             int WaitingWriters: Number of waiting writers; initially = 0
             Condition canRead = NIL
             Conditioin canWrite = NIL




                                                                             64
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                    --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 Signal(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              Signal(CanRead);                       if(--NReaders == 0)
         else                                                Signal(CanWrite);
              Signal(CanWrite);
  }                                            }




                                                                                         65
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                    --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 Signal(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              Signal(CanRead);                       if(--NReaders == 0)
         else                                                Signal(CanWrite);
              Signal(CanWrite);
  }                                            }




                                                                                         66
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                    --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 Signal(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              notify(CanRead);                       if(--NReaders == 0)
         else                                                notify(CanWrite);
              notify(CanWrite);
  }                                            }




                                                                                         67
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                            --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 notify(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              notify(CanRead);                       if(--NReaders == 0)
         else                                                notify(CanWrite);
              notify(CanWrite);
  }                                            }




                                                                                         68
             Understanding the Solution
A writer can enter if there are no other active writers and no readers
 are waiting




                                                                         69
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                            --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 notify(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              notify(CanRead);                       if(--NReaders == 0)
         else                                                notify(CanWrite);
              notify(CanWrite);
  }                                            }




                                                                                         70
                  Understanding the Solution
A reader can enter if
      There are no writers active or waiting



So we can have many readers active all at once



Otherwise, a reader waits (maybe many do)




                                                 71
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                            --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 notify(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              notify(CanRead);                       if(--NReaders == 0)
         else                                                notify(CanWrite);
              notify(CanWrite);
  }                                            }




                                                                                         72
                  Understanding the Solution
When a writer finishes, it checks to see if any readers are waiting
      If so, it lets one of them enter
      That one will let the next one enter, etc…



Similarly, when a reader finishes, if it was the last reader, it lets a
 writer in (if any is there)




                                                                          73
                           Readers and Writers

Monitor ReadersNWriters {
  int WaitingWriters, WaitingReaders,NReaders, NWriters;
  Condition CanRead, CanWrite;

 Void BeginWrite()                           Void BeginRead()
  {                                            {
        if(NWriters == 1 || NReaders > 0)           if(NWriters == 1 || WaitingWriters > 0)
        {                                           {
              ++WaitingWriters;                           ++WaitingReaders;
             wait(CanWrite);                              Wait(CanRead);
             --WaitingWriters;                            --WaitingReaders;
        }                                           }
        NWriters = 1;                               ++NReaders;
  }                                                 notify(CanRead);
  Void EndWrite()                              }
  {
         NWriters = 0;                         Void EndRead()
         if(WaitingReaders)                    {
              notify(CanRead);                       if(--NReaders == 0)
         else                                              notify(CanWrite);
              notify(CanWrite);
  }                                            }




                                                                                         74
                   Understanding the Solution
It wants to be fair
      If a writer is waiting, readers queue up
      If a reader (or another writer) is active or waiting, writers queue up

      … this is mostly fair, although once it lets a reader in, it lets ALL waiting readers in all
       at once, even if some showed up “after” other waiting writers




                                                                                                  75
                                      The Big Picture
The point here is that getting synchronization right is hard

How to pick between locks, semaphores, condvars, monitors???

Locks are very simple for many cases.
       Issues: Maybe not the most efficient solution
       For example, can't allow multiple readers but one writer inside a standard lock.

Condition variables allow threads to sleep while holding a lock
       Just be sure you understand whether they use Mesa or Hoare semantics!

Semaphores provide pretty general functionality
       But also make it really easy to botch things up.

Java captures a lot of useful common operations with its use of
 monitors (compiler checking is nice too)
       But, not possible to implement everything directly with Java's primitives.




Adapted from Matt Welsh’s (Harvard University) slides.                                     76

								
To top