Thursday, March 30, 2023

Critical section code

 Many students of Operating Systems get confused on some simple concepts such as critical section. Instead of memorizing the definition of critical section, it is better to understand it with the help of a simple example.

Suppose there is a global variable that may be accessed (i.e. read/write) by two or more threads at the same time, then the part where the two threads read/write the value of the variable needs protection. It simply means that only one thread should be able to read or update the value at a time. Otherwise, the value of the variable may end up wrong. Let us understand this problem with an example:

Let's say there is a global variable int x (with initial value 0)

Now, if you want to increment the value of the global variable two times using two threads T1 and T2. the expected value of x at the end of two increments is 2.

Now the following sequence of event may happen (with the action result after the --> ):

T1 reads value of x --> reads 0

T2 reads value of x --> reads 0

T1 increments x --- > x becomes 1

T2 increments x --- > x becomes 1 because T2 had also read 0

So, the final value of global variable x is 1, not 2. This was not what was expected. The part of the code where the two threads accessed the value of x is a critical section that needs protection.

As mentioned earlier, the critical section needs to be protected from simultaneous access by multiple threads. Mutex is a simple locking mechanism to do that. A mutex is nothing but a lock. Before entering the critical code, a thread must acquire the lock. If the lock is unavailable, the thread must wait (it is blocked). A lock is unavailable when some other thread has already acquired the lock. When the other thread goes out of critical section code, it must release the lock (unlock). The waiting thread can then enter the critical section. The above sequence of events then becomes:

T1 request the lock --> acquires lock

T1 reads value of x --> reads 0

T2 requests the lock --> blocks

T1 increments x --- > x becomes 1

T1 releases the lock --> lock released

                                --> T2 acquires the lock now

 reads value of x --> reads 1

T1 increments x --- > x becomes 2


We can see that the threads are now properly updating the global variable. The lock/unlock process is part of the program and it is the programmer's responsibility to write the code for requesting/releasing the lock. This post has become a bit long. Will give a concrete C example in the next blog. Stay tuned!

No comments:

Steps to install PyTorch on VMware workstation (Ubuntu guest)

  The following is the list of steps to install pytorch 2.0 in VMware workstation (Ubuntu guest): $ mkdir ~/pytorch $ mkdir ~/pytorch/as...