Hardware Synchronization

In Synchronization hardware, we explore several more solutions to the critical-section problem using techniques ranging from hardware to software based APIs available to application programmers. These solutions are based on the premise of locking; however, the design of such locks can be quite sophisticated.
These Hardware features can make any programming task easier and improve system efficiency. Here, we present some simple hardware instructions that are available on many systems and show how they can be used effectively in solving the critical-section problem. If we could prevent interrupts from occurring while a shared variable was being modified. The critical-section problem could be solved simply in a uniprocessor environment. In this manner, we would be assuring that the current sequence of instructions would be allowed to execute in order without preemption. No other instructions would be run, so no unexpected modifications could be made to the shared variable. This is the approach taken by non-preemptive kernels. But unfortunately, this solution is not as feasible in a multiprocessor environment. Since the message is passed to all the processors, disabling interrupts on a multiprocessor can be time consuming.
System efficiency decreases when this massage passing delays entry into each critical section. Also the effect on a system’s clock is considered if the clock is kept updated by interrupts. So many modern computer systems therefore provide special hardware instructions that allow us that we can test and modify the content of a word or to swap the contents of two words atomically—that is, as one uninterruptible unit. We may use these special instructions to solve the critical-section problem in a relatively simple manner. Now we abstract the main concepts behind these types of instructions. The TestAndSet() instruction can be defined as shown in below code.
boolean test and set(boolean *target){
   boolean rv = *target;
   *target = true;
   return rv;
}
Definition of the test and set() instruction.
The essential characteristic is that this instruction is executed atomically. So, if two TestAndSet C) instructions are executed simultaneously (each on a different CPU), they will be executed sequentially in some arbitrary order. we can implement mutual exclusion by declaring a Boolean variable lock, initialized to false, if the machine supports the TestAndSet () instruction. The structure of process P, is shown in below.
do {
   while (test and set(&lock)) ;
   /* do nothing */
   /* critical section */
   lock = false;
   /* remainder section */
}
while (true);
Mutual-exclusion implementation with test and set().
The SwapO instruction, in contrast to the TestAndSet0 instruction, operates on the contents of two words; it is executed atomically. mutual exclusion can be provided as follows if the machine supports the SwapO instruction. Here, a global Boolean variable lock is declared and is initialized to false. Additionally, each process has a local Boolean variable key. The structure of process P, is shown in figure below.
int compare_and_swap(int *val, int expected, int new val){
   int temp = *val;
   if (*val == expected)
   *val = new val;
   return temp;
}
Definition of the compare and swap() instruction.
do{
   while (compare_and_swap(&lock, 0, 1) != 0) ;
   /* do nothing */
   /* critical section */
   lock = 0;
   /* remainder section */
}
while (true);
Mutual-exclusion implementation with the compare and swap() instruction.
Since these algorithms satisfy the mutual-exclusion requirement, they do not satisfy the bounded-waiting requirement. In below code, we present another algorithm using the TestAndSet() instruction that satisfies all the critical-section requirements.
do{
   waiting[i] = true;
   key = true;
   while (waiting[i] && key)
      key = test and set(&lock);
   waiting[i] = false;
   /* critical section */
   j = (i + 1) % n;
   while ((j != i) && !waiting[j])
      j = (j + 1) % n;
   if (j == i)
      lock = false;
   else
      waiting[j] = false;
   /* remainder section */
}
while (true);
Bounded-waiting mutual exclusion with test and set().
The same data structures are boolean waiting[n]; boolean lock; These data structures are initialized to false. To prove the point that the mutual exclusion requirement is met, we note that process P; can enter its critical section only if either waiting[i] == false or key -- false. The value of key can become false only if the TestAndSet() is executed. The first process to execute the TestAndSet() will find key == false; all others must wait. The variable waiting[i] may become false only if another process leaves its critical section; only one waiting [i] is set to false, maintaining the mutual-exclusion requirement.
To prove the point that the progress requirement is met, we note that the arguments presented for mutual exclusion also apply here, since a process exiting the critical section either set lock to false or sets waiting[j] to false. Both of them allow a process that is waiting to enter its critical section to proceed. To prove the point that the bounded-waiting requirement is met, we note that, when a process leaves its critical section, it scans the array waiting in the cyclic ordering (z' + 1, i + 2,..., n — 1, 0, ..., i — 1). It designates the first process in this ordering that is in the entry section (waiting [j] =- true) as the next one to enter the critical section.
Any process that waiting to enter its critical section will thus do so within n — 1 turns. Unfortunately for hardware designers, implementing atomic TestAndSet() instructions on multiprocessors is not a trivial task.