How to do it…

  1. Define a macro of the value 10, and define an array of an equal size:
#define max 10
int stack[max];
  1. Define two mutex objects; one will be used while popping from the stack (pop_mutex), and the other will be used while pushing a value to the stack (push_mutex):
pthread_mutex_t pop_mutex;
pthread_mutex_t push_mutex;
  1. To use the stack, initialize the value of top to -1:
int top=-1;
  1. Define two variables of the type pthread_t to store two thread identifiers:
pthread_t tid1,tid2;
  1. Invoke the pthread_create function to create the first thread; the thread will be created with the default attributes. Execute the push function to create this thread:
pthread_create(&tid1,NULL,&push,NULL);
  1. Invoke the pthread_create function again to create the second thread; this thread will also be created with the default attributes. Execute the pop function to create this thread:
pthread_create(&tid2,NULL,&pop,NULL);
  1. In the push function, invoke the pthread_mutex_lock method and pass the mutex object for the push operation (push_mutex) to lock it:
pthread_mutex_lock(&push_mutex);
  1. Then, the mutex object for the pop operation (pop_mutex) will be locked by the first thread:
pthread_mutex_lock(&pop_mutex);
  1. The user is asked to enter the value to be pushed to the stack:
printf("Enter the value to push: ");
scanf("%d",&n);
  1. The value of top is incremented to 0. The value that was entered in the previous step is pushed to the location stack[0]:
top++;
stack[top]=n;
  1. Invoke pthread_mutex_unlock and unlock the mutex objects meant for the pop (pop_mutex) and push operations (push_mutex):
pthread_mutex_unlock(&pop_mutex);                                                       pthread_mutex_unlock(&push_mutex);  
  1. At the bottom of the push function, display a text message indicating that the value is pushed to the stack:
printf("Value is pushed to stack \n");
  1. In the pop function, invoke the pthread_mutex_lock function to lock the mutex object pop_mutex. It will lead to a deadlock:
pthread_mutex_lock(&pop_mutex);
  1. Again, try to lock the push_mutex object, too (although it is not possible, as it is always acquired by the first thread):
sleep(5);
pthread_mutex_lock(&push_mutex);
  1. The value in the stack, that is, pointed to by the top pointer is popped:
k=stack[top];
  1. Thereafter, the value of top is decremented by 1 to make it -1 again. The value, that, is, popped from the stack is displayed on the screen:
top--;
printf("Value popped is %d \n",k);
  1. Then, unlock the mutex object push_mutex and the pop_mutex object:
pthread_mutex_unlock(&push_mutex);     
pthread_mutex_unlock(&pop_mutex);
  1. In the main function, invoke the pthread_join method and pass the thread identifiers that were created in step 1 to it:
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);

The deadlockstate.c program for creating two threads and understanding how a deadlock occurs while acquiring locks is as follows:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

#define max 10
pthread_mutex_t pop_mutex;
pthread_mutex_t push_mutex;
int stack[max];
int top=-1;

void * push(void *arg) {
int n;
pthread_mutex_lock(&push_mutex);
pthread_mutex_lock(&pop_mutex);
printf("Enter the value to push: ");
scanf("%d",&n);
top++;
stack[top]=n;
pthread_mutex_unlock(&pop_mutex);
pthread_mutex_unlock(&push_mutex);
printf("Value is pushed to stack \n");
}
void * pop(void *arg) {
int k;
pthread_mutex_lock(&pop_mutex);
pthread_mutex_lock(&push_mutex);
k=stack[top];
top--;
printf("Value popped is %d \n",k);
pthread_mutex_unlock(&push_mutex);
pthread_mutex_unlock(&pop_mutex);
}

int main() {
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,&push,NULL);
pthread_create(&tid2,NULL,&pop,NULL);
printf("Both threads are created\n");
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}

Now, let's go behind the scenes.