Home > Back-end >  Figuring out how to deal with pthread_cond_signal to avoid threads getting stuck
Figuring out how to deal with pthread_cond_signal to avoid threads getting stuck

Time:10-04

I am struggling with multithreading concepts here.

There are two threads that access a shared struct. Idea is to have ThreadTwo acquire the lock first and sets data to true, and then ThreadOne run...which goes to sleep if data is set to true, and wake up once signalled after data being set to false.

What I'm seeing happen: ThreadOne goes to sleep, and after ThreadTwo disables data, both the threads seem to be in a deadlock state or rather they're stuck.

My understanding is: I unlock the mutex at the end of AccessShm(shmInfo, GET), and I believe pthread_cond_signal requires to have lock already acquired because it unlocks before going to sleep, and once signalled from ThreadTwo, ThreadOne wakes up, acquires a lock, and attempts to acquire again inside AccessShm but can't because it's already been acquired hence the hang?

typedef enum
{
    SET,
    GET
} Task;

typedef struct
{
    volatile bool data;     
    pthread_mutex_t pMutex;
    pthread_cond_t cv;
} ShmInfo;

bool AccessShm(ShmInfo *pShm, Task t, ...)  
{
    va_list args;
    bool setValue = true;
    bool retValue = true;

    va_start(args, t);

    pthread_mutex_lock(&pShm->pMutex);

    switch (t)
    {
        case SET:
            setValue = va_arg(args, bool);  
            pShm->data = setValue;
        
            if (setValue == false)
            {
                pthread_cond_signal(&pShm->cv); // wake up only when pShm->data is disabled
            }
            break;
        
        case GET:
            retValue = pShm->data;
            break;

        default:
            break;
    }
    pthread_mutex_unlock(&pShm->pMutex);
    return retValue;
}

void ThreadOne(void *arg)
{
    ShmInfo *shmInfo = (ShmInfo *) arg;
    while(1)
    {
        while(AccessShm(shmInfo, GET) == true) 
        {
          pthread_cond_wait(&shmInfo->cv, &shmInfo->pMutex); 
        }
        // ...
    }
}

void ThreadTwo(void *arg)
{
    ShmInfo *shmInfo = (ShmInfo *) arg;
    // ...
    AccessShm(shmInfo, SET, true);
    
    // some work/delay
    AccessShm(shmInfo, SET, false);
}

CodePudding user response:

Could it be because you're not calling va_end() (which is required) in AccessShm()?

Apparently not.

The structure of your use of the mutex and condition variable is a little unusual. Typically, the mutex is locked when pthread_cond_wait() is called in order to ensure determinism.

I suggest moving the locking/unlocking of the mutex out of AccessShm() and into both ThreadOne() and ThreadTwo(). The call to pthread_cond_signal() can be left in AccessShm() on the assumption that the mutex is locked, which you can verify via a call to assert(pthread_mutex_trylock(&pShm->pMutex)) at the beginning of that function's body.

CodePudding user response:

You are not locking the mutex associated with your condition variable before calling pthread_cond_wait().

You must only call pthread_cond_wait() with the associated mutex locked.

Per the POSIX pthread_cond_wait() documentation(bolding mine):

The pthread_cond_timedwait() and pthread_cond_wait() functions shall block on a condition variable. The application shall ensure that these functions are called with mutex locked by the calling thread; otherwise, an error (for PTHREAD_MUTEX_ERRORCHECK and robust mutexes) or undefined behavior (for other mutexes) results.

...

  • Related