"dekker's algorithm": Dekker's algorithm is the first known correct solution to the mutual exclusion problem in concurrent programming.app
/* global or shared memory */ int x = 5; int needLockT1 = 0; /* 0 or 1 */ int needLockT2 = 0; /* 0 or 1 */ int turn = T1; /* T1 or T2 */ /* Thread T1 */ /* Thread T2 */ while ( 1 ) while ( 1 ) { { execNonCriticalSection(); execNonCriticalSection(); needLockT1 = 1; needLockT2 = 1; turn = T2; turn = T1; // compete for the 'turn' while ( turn == T2 && while ( turn == T1 && needLockT2 == 1 ) needLockT1 == 1 ) { { /* busy wait */ /* busy wait */ } } execCriticalSection(); execCriticalSection(); needLockT1 = 0; needLockT2 = 0; } } T1: needLockT1 = 1; T1: turn = T2; T2: needLockT2 = 1; T2: turn = T1; T1: execCriticalSection(); T1: needLockT1 = 0; T2: execCriticalSection();
"a system of sending messages by holding the arms or two flags or poles in certain positions according to an alphabetic code."less
-- an OS construct that enables us to have synchronized access
to one or more shared resourcesoop
-- special non-negative int variableui
-- two operations:this
(1) first operation essentially attempts to gain accesscode
P() proberen (to try) wait() down()
(2) second operation relinquishes the access the acquiredblog
V() vrijgeven (to release) signal() up()
semaphore S is non-negative int variablessl
P( semaphore S ) /* this P() operation MUST execute without */ { /* any interruption, i.e., no context switch */ while ( S == 0 ) /* resource count*/ /* between exiting the while() loop and */ { /* executing S-- */ /* busy wait */ } S--; } V( semaphore S ) { S++; }
-- Given a shared buffer (i.e., array) of a fixed size n
-- One or more producer threads
-- One or more consumer threadsci
/* shared/global memory */ int n = 20; buffer[n]; semaphore empty_slots = n; semaphore used_slots = 0; semaphore mutex = 1; /* producer */ /* consumer */ while ( 1 ) while ( 1 ) { { item = produce_next_item(); P( used_slots ); P( empty_slots ); P( mutex ); P( mutex ); item = remove_from_buffer(); add_to_buffer( item ); V( mutex ); V( mutex ); V( empty_slots ); V( used_slots ); consume( item ); } }
uses two counting semaphores to ensure:rem
(1) no buffer overflow will occur in a producer
(2) no reading from an empty buffer in the consumer
Uses mutex to ensure the add/remove could be exclusively happening at the same time.
"A Mutex is different than a semaphore as it is a locking mechanism while a semaphore is a signalling mechanism. A binary semaphore can be used as a Mutex but a Mutex can never be used as a semaphore."
Given: five philosophers that engage in only two activities:
-- thinking (i.e., independent computation)
-- eating (i.e., sharing a resource; therefore, requires synchronization)
Given: shared table with five bowls and five chopsticks,
and a bowl of food in the middle of the table
(which is endlessly replenished)
Key contraint: to eat(), a philosopher must obtain two chopsticks,
one from the left, one from the right
First attempt:
chopstick is array[5] of semaphores
philosopher( i ) /* i in 0..4 */ { while ( 1 ) { think() P( chopstick[i] ) //DEADLOCK P( chopstick[i+1%5] ) eat() /* critical section */ V( chopstick[i+1%5] ) V( chopstick[i] ) } }
Second attempt:
chopstick is array[5] of semaphores
philosopher( i ) /* i in 0..4 */ { while ( 1 ) { think() P( mutex ); // top-level mutex -- NOT EFFICIENT P( chopstick[i] ) P( chopstick[i+1%5] ) V( mutex ); eat() /* critical section */ V( chopstick[i+1%5] ) V( chopstick[i] ) } }
Third attempt:
-- use an asymmetric solution
chopstick is array[5] of semaphores
philosopher( i ) /* i in 0..3 (instead of i in 0..4) */ { while ( 1 ) { think() P( chopstick[i] ) P( chopstick[i+1%5] ) eat() /* critical section */ V( chopstick[i+1%5] ) V( chopstick[i] ) } } philosopher( i ) /* i is always 4 */ /*it holds the slots required as the "left" chopsticks for both its nbrs*/ { while ( 1 ) { think() P( chopstick[i+1%5] ) /* we swapped the order of the P() operations */ P( chopstick[i] ) eat() /* critical section */ V( chopstick[i] ) V( chopstick[i+1%5] ) } }
Deadlock: We have deadlock when no process/thread can make any
further progress (i.e., all blocked on P() operation
and the given resource will NEVER become available)
Deadlock requires four conditions:
-- mutual exclusion
-- hold and wait
-- no preemption
-- circular wait -- i.e., a cycle in resource allocation graph
Deadlock:
"classical circular"
circular: P1, P2, R1, R2