c++multithreadingpthreadsdeadlockcritical-section

Deadlocked program using pthread condition variables, critical sections [C++]


What I'm trying to do is have each thread copy information from a struct in main using a critical section before main changes the struct for other threads.

#include <iostream>
#include <pthread.h>

using namespace std;

struct foo
{
public:
   int var;
   int *turn;
   int index;
   pthread_mutex_t *bsem;
   pthread_cond_t *waitTurn;
};
void *threadFunc(void *arg) {
    int var;
    foo *workingVar = (foo *)arg;
    pthread_mutex_lock(workingVar->bsem);
    while (*workingVar->turn != workingVar->index)
        pthread_cond_wait(workingVar->waitTurn, workingVar->bsem);
    var = workingVar->var;
    *workingVar->turn++;
    pthread_cond_broadcast(workingVar->waitTurn);
    pthread_mutex_unlock(workingVar->bsem);
    cout << var << endl;
    return nullptr;
}
int main() {
   int turn = 0, NTHREADS = 5;
   foo mainStruct;
   pthread_mutex_t bsem;
   pthread_mutex_init(&bsem, NULL);
   pthread_cond_t waitTurn = PTHREAD_COND_INITIALIZER;
   mainStruct.bsem = &bsem;
   mainStruct.waitTurn = &waitTurn;
   mainStruct.turn = &turn;
   pthread_t tid[NTHREADS];
   for (int i = 0; i < NTHREADS; i++) {
   mainStruct.index = i;
   mainStruct.var = i;
   pthread_create(&tid[i], nullptr, threadFunc, &mainStruct);
   }
   for (int i = 0; i < NTHREADS; i++)
      pthread_join(tid[i], NULL);
}

The code above is from a thread function. I'm trying to make threads wait until it is their 'turn' using pthread_cond_wait, and then once the condition is true, the struct info passed from main will be copied into local variables and will increment the turn and exit the critical section (also not using global variables, so mutex and condition variable is passed using a pointer through the struct). Turn is initialized to 0, and index is the thread number (in the order of which it was created).

This process is deadlocked, and times out.

Please let me know if any more context/information is needed, this is my first Stack Overflow question.


Solution

  • I see race condition on mainStruct.index. Main thread modifies it without lock and multiple threads are accessing it under a lock. So if first thread starts fast it will interact with your loop spawning threads.

    Looks like you plan was to have multiple instances of Foo, but you have one. So imagine scenario:

    1. Main thread reaches first join
    2. Other threads were created, but didn't start ruinng yet
    3. At that point value of mainStruct.index is 4!
    4. First thread starts executing and acqurie lock.
    5. turn is 0 and workingVar->index is 4 (see point 3)
    6. So threads enters pthread_cond_wait and waits
    7. Next thread executes same as points 4-6
    8. Same for other threads
    9. All side threads are in waiting state

    All threads are waiting no one has called pthread_cond_broadcast condition *workingVar->turn != workingVar->index is always true

    Also your code is written in C not in C++. The only C++ is use of std::cout!. Try rewrite this using std::thread and it will fix itself (if you pas arguments by values).

    Here is my C++ version