Search code examples
mutexmutual-exclusion

Why is this Mutex solution incorrect?


There are 2 processes P1 and P2 that can enter the Critical Section.

Mutex Solution Requirements:

A Mutex Zone - (Critical Section) that can only hold one process maximum.

No Mutual Blocking - a process outside the critical section cannot block a process inside it.

No Starvation - a process interested in entering the critical section must not have to wait forever.

Success without Contention - a process interested in entering the critical section must succeed in doing so if there are no other processes interested.

Why is the below code an incorrect solution to the Mutual Exclusion problem?

i.e. which requirement does it not satisfy?

C1 and C2 are initialised to 1.

P1:  LOOP
        Non-Critical Section
        LOOP UNTIL C2 = 1 END_LOOP;
        C1 := 0;
        Critical Section
        C1 := 1;
END

P2:  LOOP
        Non-Critical Section
        LOOP UNTIL C1 = 1 END_LOOP;
        C2 := 0;
        Critical Section
        C2 := 1;
END

Solution

  • To interpret the question with the best of intentions, I would have to assume that each read or write is deemed to be atomic and well-ordered. Then it makes more sense.

    The problem you have here is that the inner loop in each process can independently complete. Those loops are the "wait for my turn" part of the mutex.

    However, the terminating of that loop and the following assignment (which would stop the other loop from terminating) are not atomic. We therefore have the following possible scenario:

    P1: exit wait loop because C2 is 1
    
    P2: exit wait loop because C1 is 1
    P2: set C2 to 0
    P2: enter critical section
    
    P1: set C1 to 0
    P1: enter critical section
    

    The above violates that first requirement of having a Mutex Zone. Conditions that lead to such a violation are commonly known as a race condition.

    You could also expect one process to starve the other. There is a possibility that P2 will always execute its critical section and acquire the lock again before P1 (who is waiting for the lock) gets a slice of CPU time. The control variable C2 would therefore always be 0 as seen by P1. Or at least, may be that way for a disproportionate number of slices.

    P2: exit wait loop because C1 is 1
    P2: set C2 to 0
    
    P1: spin on C2 != 1 for entire time slice
    
    P2: enter critical section
    P2: set C2 to 1
    P2: exit wait loop because C1 is 1
    P2: set C2 to 0
    
    P1: spin on C2 != 1 for entire time slice
    
    P2: enter critical section
    P2: set C2 to 1
    P2: exit wait loop because C1 is 1
    P2: set C2 to 0
    
    P1: spin on C2 != 1 for entire time slice
    
    ...
    

    Except in some environments, it's unlikely that P1 would be starved forever. But since P1 has asserted that it is waiting, it should not expect P2 to get multiple cracks at the critical section before it gets a turn.

    This might also be a violation of the Success Without Contention requirement, but that's hard to argue really. But I would suggest that if P2 is no longer running, then consider what state C2 might be left in, and why P1 should need to know about P2 at all.