A race condition occurs when two or more threads can access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any time, you don't know the order in which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are "racing" to access/change the data.

Blow are some examples of race condition:

Deadlocks
Shared Resource
Temp Files in Shell Scripts
Time of Check to Time of Use
Unsafe Functions in Signal Handlers

Deadlock in a multi-threaded program

Definitions:

Mutex - A mechanism supplied by threading libraries that can be used to make a block of code be executed atomically.
Deadlock - Locking resources such that they will never become unlocked, causing multiple threads of execution to stop.

What You Should Not Do

Causing a deadlock to occur. They can be incredibly difficult to detect in situations where many mutexes/semaphors are used.

mutex A;
mutex B;

void thread1()
{

    do something...
	
    lock(A);
        lock(B);

            do something...

        unlock(B);
    unlock(A)
}

void thread2()
{
    do something...

    lock(B);
        lock(A);

            do something...

        unlock(A);
    unlock(B);
}

The above example is a trivialization of what could go wrong. If the following sequence of events were to happen, the above threads would go into a deadlock:

thread1: lock(A);
thread2: lock(B);    // no deadlock yet

thread2: lock(A);    // thread2 is blocked until A is unlocked

thread1: lock(B);    // thread1 is blocked until B is unlocked, but thread2 
                     // won't unlock B until A is unlocked, because thread1
                     // locked A. Thus, neither thread of execution can proceed

What You Should Do

Deadlocks in more complicated cases can be difficult to detect. About the only way to prevent them is to design the code to never let it happen.

The above example could be fixed by replacing both mutexes with a single mutex, assuming there isn't an important reason for having the two mutexes.

Design strategies usually recommend minimizing the amount of code that needs the mutexes' protection, and isolating it. This is the basic idea behind monitors.

Concept Map

This example refers to the point 6 in the Concept Map.

 
 

 

 Accessing shared resources (unlocked) in the multi-threaded program

What You Should Not Do

You need to access a shared variable in a multithreaded program, so you access the value directly, without any sort of locking. For example:

transferAmount = GetTransferAmount();
balance = GetBalanceFromDatabase();
 
if (transferAmount < 0) {
	printf("Bad Transfer Amount\n");
	exit(0);
}
newBalance = balance - transferAmount;
if ((balance - transferAmount) < 0) {
	printf("Insufficient Funds\n");
	newBalance = balance;
}
SendNewBalanceToDatabase(newBalance);
printf("Transfer of %f succeeded.\n",transferAmount);
NotifyUser("New balance: %f\n", newBalance);

In this example, we have no assurance that one instance of this user will get the correct transferAmount in either of the if-statements. Since, these statements control the flow of our program (and determine how much money is passed around) we could get situations where one instance transfers all of the money while another instance still has a full balance.

What You Should Do

If a resource is shared between threads and each thread has a function that may change the resource state, you need a way to ensure that that resource will remain in the state expected by each state. This can be achieved by using synchronization primitives when available (such as 'synchronized' in Java), mutexes and atomic operations, or simply avoiding sharing resources across threads. Taking our example from above:

transferAmount = GetTransferAmount();

if( accountBusy )
	wait accountFree
else {
	signal accountBusy
	balance = GetBalanceFromDatabase();
 
	if (transferAmount < 0) {
		printf("Bad Transfer Amount\n");
		exit(0);
	}
	newBalance = balance - transferAmount;
	if ((balance - transferAmount) < 0) {
		printf("Insufficient Funds\n");
		newBalance = balance;
	}
	SendNewBalanceToDatabase(newBalance);
	signal accountFree
}
printf("Transfer of %f succeeded.\n",transferAmount);
NotifyUser("New balance: %f\n", newBalance);

Since we ensure that the shared resources can only be accessed by one thread at a time, we ensure that the state remains consistent between threads.

Concept Map

This example refers to the point B and iv in the Concept Map.

 Avoid race condition while writing to a file using shell script

What You Should Not Do

NOTE! THIS FILE NEEDS TO BE REDONE!
#!/bin/sh
echo "This is a test" > /tmp/test$$
# Someone can manipulate or alter the file between these calls
cat < /tmp/test$$

What You Should Do

Use pipelines or your home directory. Ordinary shells usually don't support file descriptors.
#!/bin/sh
echo "This is a test" | cat

Concept Map

This example refers to the point B and 7 in the Concept Map.

 

 

Use the file descriptor to avoid race conditions

Definitions:

Time of Check to Time of Use - (TOCTTOU − pronounced "TOCK too") A race condition where a problem/attack occurs between when a resource is checked and when it is actually used.

What You Should Not Do

You check a file to see if it meets some criteria before you open and use it. Meanwhile, the file is replaced with another file, so the check is no longer valid.
struct stat mystat;
int myfd;
if(stat("filename",&mystat) != 0) // get the file's info to check error stat()'ing the file!
do checks on the file's properties      // the file could be swapped out/
                                        // modified in here

if(the checks are ok)
{
if((myfd=open("filename") != 0) // open the file (use it) error getting fd to file!
    do something with the file          // use the file
}

What You Should Do

Some systems allow you to lock the file on the operating system level. Many systems allow you to lock a file, but the lock is only honored by programs that respect the lock. This includes UNIX and the Mac OS. Depending on the application, it could be solved by opening a pointer to the file and doing all checks from that instead of the file name. That way, if the file gets swapped out for another file, the file pointer/descriptor still refers to the same object. (which the file name does not necessarily point to)
struct stat mystat;
int myfd;
if((myfd=open("filename", "r")) != 0) //open the file to get a file descriptor error getting fd to file! if(fstat(myfd,&mystat) != 0) // fstat(): get the file's info // using the fd error stat'ing the file!
do checks on the file's properties      // if someone swaps in their own 
                                        // file, it won't sneak in on the
                                        // program anymore

if(the checks are ok)
{
    do something with the file          // use the file
}   

This change will ensure that the file is the same file between the check and use. (It prevents someone from swapping in another file) It will not prevent someone changing the file's permissions to allow someone else to read/write the file between the check and use. 

Concept Map

This example refers to the point B and 6 in the Concept Map.

 

Unsafe functions cause race conditions

Definitions:

Unsafe function - A function that cannot be interrupted during use, and then called again; in other words, it is non-reentrant. This can be due to use of global variables, or due to calls to malloc() and free().

What You Should Not Do

Using an unsafe function non-atomically in a program that has signal handlers. For example, calling free() on certain global variables, as well as calling unsafe functions that utilize malloc()/free(), such as syslog() from a signal handler. If these calls get interrupted and called again, it is possible for them to execute arbitrary code.

What You Should Do

Not using unsafe function calls at all in signal handlers is safest. Executing every use of a particular unsafe function atomically by blocking signals that call handlers that could cause them to be reentered may also be safe.

 

 

 

 

JSN Teki template designed by JoomlaShine.com