Search code examples
cpthreadsdeadlock

Practice program not making deadlock as intended


I'm trying to learn about deadlocks in my operating systems class, and, no matter how many times I run it, the program I made isn't deadlocking. The code is:

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

int resource1;
int resource2;
void *causeDeadlock1();
void *causeDeadlock2();
void wait(int r);
void signal(int r);
pthread_t tid[2];

int main()
{
    resource1 = 1;
    resource2 = 1;
    pthread_create((&tid[0]), NULL, &causeDeadlock1, NULL);
    pthread_create((&tid[1]), NULL, &causeDeadlock2, NULL);
    return(0);
}

void wait(int r)
{
    while(r<1);
    printf("Done waiting\n");
    r--;
}

void signal(int r)
{
    r++;
}

void* causeDeadlock1()
{
    wait(resource1);
    wait(resource2);

    printf("Thread 1 is running with both resources.\n");

    signal(resource1);
    signal(resource2);

    printf("Thread 1 is done.\n");
}

void *causeDeadlock2()
{
    wait(resource2);
    wait(resource1);

    printf("Thread 2 is running with both resources.\n");

    signal(resource2);
    signal(resource1);

    printf("Thread 2 is done.\n");
}

When I run it, I commonly see no output, but I'm also returned to console normally. I would think the reason that is is that the threads never get to print that they have the resources or that they're done because they get stuck forever in the wait function's while loop. However, if that's the case, shouldn't the program hang instead of returning me to console like nothing happened?


Solution

  • There are three issues with your program. EDIT: Four, if you also count B. Wolf's answer.

    First of all, your wait and signal functions don't cause any data races. They both affect not the real resource (int r), but the copy passed as a parameter. Remember that C uses call-by-value. You should fix that, e.g.

    void wait(int *r) {
      while (*r<1);       // still wrong, see below!
      (*r)--;
    }
    
    void signal(int *r) {
      (*r)++;
    }
    
    ... wait(&resource1);
    ... signal(&resource1);
    

    Second, even if wait is changed as above, the test *r >= 1 that exits the while loop and the decrement operation should be performed atomically. Otherwise, there is a chance that two or more threads acquire the resource at the same time and the semantics of your wait action is flawed.

    Third, even if you fix the second issue too, your program may deadlock, it is not guaranteed to deadlock. Deadlock will happen only if the threads' actions are interleaved in the following way:

    1. causeDeadlock1 acquires resource1
    2. causeDeadlock2 acquires resource2
    3. causeDeadlock1 waits for resource2
    4. causeDeadlock2 waits for resource1

    where it is important that 1 and 2 (in any relative order) happen before 3 and 4 (again in any relative order).

    Most often, I would expect that a thread acquiring its first resource would manage to acquire the second resource too, before the other thread even has a chance to start executing. Therefore, I would not bet on a deadlock, if you just run this once. Put the bodies of functions causeDeadlock1 and causeDeadlock2 in loops and you'll definitely see a deadlock (after you fix the other two issues).