Many systems provide hardware support for critical section
Uniprocessors - could disable interrupts
Currently running code would execute without pre-emption.
Generally too inefficient on multiprocessor systems
Delay in one processor telling others to disable their interrupts
Modern machines provide the special atomic hardware instructions (Atomic = non-interruptible) TestAndSet or Swap which achieve the same goal:
TestAndSet : Test memory address (i.e. read it) and set it in one instruction
Swap : Swap contents of two memory addresses in one instruction
We can use these to implement simple locks to realise mutual exclusion.
The general pattern for locks is :
do {
[acquire lock]
[critical section]
[release lock]
[remainder section]
} while (TRUE);
TestAndSet Instruction
boolean TestAndSet (boolean *target) { boolean original = *target; // Store the original value *target = TRUE; // Set the variable to TRUE return original: // Return the original value}
In a nutshell : this single CPU instruction sets a variable to TRUE and returns the original value.
This is useful because, if it returns FALSE, we know that only our thread has changed the value from FALSE to TRUE; if it returns TRUE, we know we haven’t changed the value.
Solution using TestAndSet
Shared boolean variable lock, initialised to false
Solution :
do { while (TestAndSet(&lock)) ; // wait until we successfully // change lock from false to true [critical section] lock = FALSE; // Release lock [remainder section]} while (TRUE);
Note, though, that this achieves mutual exclusion but not bounded waiting - one process could potentially wait for ever due to the unpredictability of context switching.
Bounded-waiting Mutual Exclusion with TestAndSet()
All data structures are initialised to FALSE.
wants_in[] is an array of waiting flags, one for each process.
lock is a boolean variable used to lock the critical section.
boolean wants_in[], key = FALSE, lock = FALSE; //all false to begin withdo { wants_in[i] = TRUE; // I (process i) am waiting to get in. key = TRUE; // Assume another has the key. while (wants_in[i] && key) { // Wait until I get the lock // (key will become false) key = TestAndSet(&lock); // Try to get the lock } wants_in[i] = FALSE; // I am no longer waiting: I'm in now [*** critical section ***] // Next 2 lines: get ID of next waiting process, if any. j = (i + 1) % NO_PROCESSES; // Set j to my right-hand process. while ((j != i) && !wants_in[j]) { j = (j + 1) % NO_PROCESSES }; if (j == i) { // If no other process is waiting... lock = FALSE; // Release the lock } else { // else .... wants_in[j] = FALSE; // Allow j to slip in through the 'back door' } [*** remainder section ***]} while (TRUE);