We will first define a macro called max of the value 10, along with an array stack of the size max. Then, we will define two mutex objects with the names pop_mutex and push_mutex. To use the stack, we will initialize the value of top to -1. We will also define two variables of the type pthread_t, with the names tid1 and tid2, to store two thread identifiers.
We will invoke the pthread_create function to create the first thread, and we will assign the identifier returned by the function to the variable tid1. The thread will be created with the default attributes, and we will execute the push function to create this thread.
We will invoke the pthread_create function again to create the second thread, and we will assign the identifier returned by the function to the variable tid2. This thread is also created with the default attributes, and we will execute the pop function to create this thread. On the screen, we will display the message Both threads are created.
In the push function, we will invoke the pthread_mutex_lock method and pass the mutex object push_mutex to it to lock it. Now, if any other thread asks for the push_mutex object, it will need to wait until the object is unlocked.
Then, the mutex object pop_mutex will be locked by the first thread. We will be asked to enter the value to be pushed to the stack. The entered value will be assigned to the variable n. The value of top will be incremented to 0. The value that we enter will be pushed to the location stack[0].
Next, we will invoke the pthread_mutex_unlock and pass the mutex object pop_mutex to it to unlock it. Also, the mutex object push_mutex will be unlocked. At the bottom of the push function, we will display the message Value is pushed to stack.
In the pop function, the mutex object pop_mutex will be locked, and then it will try to lock the push_mutex object that is already locked by first thread. The value in the stack, that is, pointed at by the pointer top will be popped. Because the value of top is 0, the value at the stack[0] location will be picked up and assigned to the variable k. Thereafter, the value of top will decrement by 1 to make it -1 again. The value, that is, popped from the stack will be displayed on the screen. Then, the mutex object push_mutex will be unlocked, followed by unlocking the pop_mutex object.
In the main function, we will invoke the pthread_join method twice and pass the tid1 and tid2 thread identifiers to it. The reason that we invoke the pthread_join method is to make the two threads and the main method wait until both of the threads have completed their tasks.
In this program, a deadlock has occurred because in the push function, the first thread locked the push_mutex object and tried to get the lock of the pop_mutex object, which was already locked by the second thread in the pop function. In the pop function, the thread locked the mutex object pop_mutex and tried to lock the push_mutex object, which was already locked by the first thread. So, neither of the threads will be able to finish, and they will keep waiting indefinitely for the other thread to release its mutex object.
Let's use GCC to compile the deadlockstate.c program, as follows:
D:\CBook>gcc deadlockstate.c -o deadlockstate
If you get no errors or warnings, that means the deadlockstate.c program is compiled into an executable file, deadlockstate.exe. Let's run this executable file:
You've now seen how a deadlock can occur. Now, let's move on to the next recipe!