0

I wonder what is the best practice in my situation. in the following code i signal the writer before unlocking the mutex

void* Reader(void *context)
{
    while(TRUE)
    {
        LOCK(&g_mutex);
        ++g_activeReaders;
        UNLOCK(&g_mutex);

        printf("reader: %ld counter val: %d ", (long)context, g_counter);

        LOCK(&g_mutex);
        --g_activeReaders;
        printf("g_activeReaders: %d \n", g_activeReaders);
        if(0 == g_activeReaders)
        {
            SIGNAL(&g_cv);
        }
        UNLOCK(&g_mutex);
    }

    return NULL;
}

I wonder if this is better for some reason, to prevent deadlock

void* Reader(void *context)
{
    int signalFlag;
        while(TRUE)
    {
                signalFlag = 0;
        LOCK(&g_mutex);
        ++g_activeReaders;
        UNLOCK(&g_mutex);

        printf("reader: %ld counter val: %d ", (long)context, g_counter);

        LOCK(&g_mutex);
        --g_activeReaders;
        printf("g_activeReaders: %d \n", g_activeReaders);
        if(0 == g_activeReaders)
        {
                        signalFlag = 1;
        }
        UNLOCK(&g_mutex);

                if(signalFlag)
                SIGNAL(&g_cv);
    }


    return NULL;
}

my complete program is:

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

pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t g_cv = PTHREAD_COND_INITIALIZER;
int g_activeReaders = 0;
int g_counter;

#define LOCK(X) pthread_mutex_lock(X)
#define UNLOCK(X) pthread_mutex_unlock(X)
#define WAIT pthread_cond_wait
#define WAKE_ALL pthread_cond_broadcast
#define SIGNAL pthread_cond_signal
#define JOIN pthread_join
#define TRUE 1
#define N 3

void* Writer(void * context)
{
    while(TRUE)
    {
        LOCK(&g_mutex);
        while(g_activeReaders)
        {
            WAIT(&g_cv, &g_mutex);
        }

        ++g_counter;
        UNLOCK(&g_mutex);
    }

    return NULL;
}

void* Reader(void *context)
{
    while(TRUE)
    {
        LOCK(&g_mutex);
        ++g_activeReaders;
        UNLOCK(&g_mutex);

        printf("reader: %ld counter val: %d ", (long)context, g_counter);

        LOCK(&g_mutex);
        --g_activeReaders;
        printf("g_activeReaders: %d \n", g_activeReaders);
        if(0 == g_activeReaders)
        {
            SIGNAL(&g_cv);
        }
        UNLOCK(&g_mutex);
    }

    return NULL;
}

void InitWriters(pthread_t* writers, int count)
{
    int status;
    int i;

    for(i = 0; i < count; ++i)
    {
        status = pthread_create(&writers[i], NULL, Writer, NULL);

        if(status)
        {
            fprintf(stderr, "Writer create fail\n");
            exit(-1);
        }
    }
}

void InitReaders(pthread_t* readers, int count)
{
    int status;
    int i;

    for(i = 0; i < count; ++i)
    {
        long e = i;
        status = pthread_create(&readers[i], NULL, Reader, (void*) e);

        if(status)
        {
            fprintf(stderr, "readers create fail\n");
            exit(-1);
        }
    }
}

void JoinThreads(pthread_t* threads, int count)
{
    int status;
    int i;

    for(i = 0; i < count; ++i)
    {
        status = pthread_join(threads[i], NULL);

        if(status)
        {
            fprintf(stderr, "readers create fail\n");
            exit(-1);
        }
    }
}

int main(void)
{
    pthread_t writer;
    pthread_t readers[N];

    InitWriters(&writer, 1);
    InitReaders(readers, N);

    JoinThreads(&writer, 1);
    JoinThreads(readers, N);

    pthread_cond_destroy(&g_cv);
    pthread_mutex_destroy(&g_mutex);

    return 0;
}

my implementation is based upon pseudo code found (here)[Reader/Writer implementation in C

avish
  • 37
  • 7

1 Answers1

0

It does not matter for correctness - either is fine, and there is no deadlock.

In the case where you are using different thread priorities, the implementation where the condition variable is signalled before unlocking the mutex can ensure that a higher-priority thread that is waiting on the condition variable will get the mutex in preference to a lower-priority thread that is just waiting on the mutex.

caf
  • 233,326
  • 40
  • 323
  • 462
  • thanks for the answer, could you explain how the mechanism for the higher-priority to acquire the mutex works? – avish Jul 14 '19 at 14:38
  • @avish: If you signal while still holding the mutex, one of the threads waiting on the condition variable can be re-queued on to the mutex itself, so when you unlock the mutex it can be selected to run. If instead you unlock the mutex first, one of the threads already waiting on the mutex can be selected to run at that time, and by the time the condition variable is signalled, the mutex has already been retaken by that other thread. – caf Jul 14 '19 at 14:48