Show how this works using the causality diagram Click for diagram in postscript or html.

---------------- Shared Memory Coordination ----------------

See chapter 2 of tannenbaum

We will be looking at processes coordination using SHARED MEMORY and
BUSY WAITING.

    So we don't send messages but read and write shared variables.

    When we need to wait, we loop and don't context switch

        Can be wasteful of resourses if must wait a long time.

        Context switching primitives normally use busy waiting in
        their implementation.

Mutual Exclusion

    Consider adding one to a shared variable V.

    When compiled onto many machines get three instructions

        load  r1 <-- V
        add   r1 <-- r1+1
        store r1 --> V

    Assume V is initially 10 and one process begins the 3 inst seq

        after the first instruction context to another process

            registers are of course saved

        new process does all three instructions

        context switch back

            registers are of course restored

        first process finishes

    V has been incremented twice but has only reached 11

    Problem is the 3 inst seq must be atomic, i.e. cannot be
    interleaved with another execution of these instructions

    That is one execution excludes the possibility of another.  So the
    must exclude each other, i.e. mutual exclusion.

    This was a RACE CONDITION.

        Hard bugs to find since non-deterministic.

    Can be more than two processes

    The portion of code that requires mutual exclusion is often called
    a CRITICAL SECTION.

HOMEWORK 2.2 2.3 2.4

One approach is to prevent context switching

    Do this for the kernel of a uniprocessor

        Mask interrupts

    Not feasible for user mode processes

    Not feasible for multiprocessors

CRITICAL SECTION PROBLEM is to implement

    loop
        trying-part
        critical-section
        releasing-part
        non-critical section

So that when many processes execute this you never have more than one
in the critical section

That is you must write trying-part and releasing-part

Trivial solution.

    Let releasing part be simply "halt"

This shows we need to specify the problem better

Additional requirement

    Assume that if a process begins execution of its critical section
    and no other process enters the critical section, then the first
    process will eventually exit the critical section

    Then the requirement is "If a process is executing its
    trying part, then SOME process will eventually enter the critical
    section".

---------------- Software-only solutions to CS problem ----------------

We assume the existence of atomic loads and stores

    Only upto wordlength

We start with the case of two processes

Easy if want tasks to alternate in CS and you know which one goes
first in CS

            Shared int turn = 1

loop                                  loop
    while (turn=2)  --EMPTY BODY!!        while (turn=1)
    CS                                    CS
    turn=2                                turn=1
    NCS                                   NCS


But always alternating does not satisfy the additional requirement
above.  Let NCS for process 1 be an infinite loop (or a halt).
Will get to a point when process 1 is in its trying part but
turn=2 and turn will not change.  So some process enters its
trying part but neither proc will enter the CS.

Some attempts at a general soln follow

First idea.  The trouble was the silly turn

           Shared bool P1wants=false, P2wants=false

loop                                  loop
    P1wants <-- true                      P2wants <-- true
    while (P2wants)                       while (P1wants)
    CS                                    CS
    P1wants <-- false                     P2wants <-- false
    NCS                                   NCS

This fails.  Why?  This kind of question is very important and
makes a good exam question.

Next idea.  It is easy to fix the above

           Shared bool P1wants=false, P2wants=false

loop                                  loop
    while (P2wants)                       while (P1wants)
    P1wants <-- true                      P2wants <-- true
    CS                                    CS
    P1wants <-- false                     P2wants <-- false
    NCS                                   NCS

This also fails.  Why??  Show on board what a scenario looks like.

Next idea need a second test.

           Shared bool P1wants=false, P2wants=false

loop                                  loop
    while (P2wants)                       while (P1wants)
    P1wants <-- true                      P2wants <-- true
    while (P2wants)                       while (P1wants)
    CS                                    CS
    P1wants <-- false                     P2wants <-- false
    NCS                                   NCS

Guess what, it fails again.

The first one that worked was discovered by a mathematician named
Dekker.  Use turn only to resolve disputes.

           Shared bool P1wants=false, P2wants=false
           Shared int  turn=1

loop                                loop
    P1wants <-- true                    P2wants <-- true
    while (P2wants)                     while (P1wants)
        if turn=2                           if turn=1
            P1wants <-- false                   P2wants <-- false
            while (turn==2)                     while (turn=1)
            P1wants <-- true                    P2wants <-- true
    CS                                  CS
    turn <-- 2                          turn <-- 1
    P1wants <-- false                   P2wants <-- false
    NCS                                 NCS

First two lines look like deadlock country.  But it is not an
empty while loop.

    The winner-to-be just loops waiting for the loser to give up
    and then goes into the CS.

    The loser-to-be

        Gives up

        Waits to see that the winner has finished

        Starts over (knowing it will win)

Dijkstra extended dekker's soln for > 2 processes

Others improved the fairness of dijkstra's algorithm

These complicated methods remained the simplest known until 1981
when Peterson found a much simpler method

Keep dekker's idea of using turn only to resolve disputes, but
drop the complicated then body of the if.

           Shared bool P1wants=false, P2wants=false
           Shared int  turn=1

loop                                loop
    P1wants <-- true                    P2wants <-- true
    while (P2wants and turn=2)          while (P1wants and turn=1)
    CS                                  CS
    turn <-- 2                          turn <-- 1
    P1wants <-- false                   P2wants <-- false
    NCS                                 NCS


This might be correct!

The standard solution from peterson just has the assignment to
turn moved from the trying to the releasing part

           Shared bool P1wants=false, P2wants=false
           Shared int  turn=1

loop                                loop
    P1wants <-- true                    P2wants <-- true
    turn <-- 2                          turn <-- 1
    while (P2wants and turn=2)          while (P1wants and turn=1)
    CS                                  CS
    P1wants <-- false                   P2wants <-- false
    NCS                                 NCS

Remarks

    Peterson's paper is in Inf. Proc. Lett. 12 #3 (1981) pp
    115-116.

    A fairly simple proof of its correctness is by Hofri in
    OS Review Jan 1980 pp 18-22.  I will put a copy in the courant
    library in the box for this course.

    Peterson actually showed the result for n processes (not just
    2).

    Hofri's proof also shows that the algorithm satisfies a strong
    fairness condition, namely LINEAR WAITING.

        Assuming a process continually executes, other cannot pass
        it (see hofri for details).