tags:

views:

679

answers:

2

I have two threads, using C pthreads on linux. One of them writes data and the other is reading it. I'm using a variable to allow the read thread when is allowed to read and the write one when is allowed. So the mutex applies to this boolean variable called "newData". My question is: do I need to lock/unlock the mutex around the accesses inside the "if" condition? Both ways work, but I think just because the chances of overlapping a write/read over a this variable are very few. I show both alternatives to explain better my question:

Thread 1:

pthread_mutex_lock( &lattice_mutex );
if (!newData) {
    pthread_mutex_unlock( &lattice_mutex );
    uchar *lattice_pos = lattice;
    int i;
    for(i=0; i<size; i++) {
        *lattice_pos = rand()%CHAR_MAX;
        lattice_pos++;
    }
    pthread_mutex_lock( &lattice_mutex );
    newData = TRUE;
    pthread_mutex_unlock( &lattice_mutex );
} else {
    pthread_mutex_unlock( &lattice_mutex );
}

Thread 2:

pthread_mutex_lock( &lattice_mutex );
if(newData) {
    pthread_mutex_unlock( &lattice_mutex );
    renderUpdate();
    pthread_mutex_lock( &lattice_mutex );
    newData = FALSE;
    pthread_mutex_unlock( &lattice_mutex );
} else {
    pthread_mutex_unlock( &lattice_mutex );
}

Second version, which works but I don't know if it is correct:

Thread 1:

if (!newData) {
    uchar *lattice_pos = lattice;
    int i;
    for(i=0; i<size; i++) {
        *lattice_pos = rand()%CHAR_MAX;
        lattice_pos++;
    }
    pthread_mutex_lock( &lattice_mutex );
    newData = TRUE;
    pthread_mutex_unlock( &lattice_mutex );
}

Thread 2:

if(newData) {
    renderUpdate();
    pthread_mutex_lock( &lattice_mutex );
    newData = FALSE;
    pthread_mutex_unlock( &lattice_mutex );
}
+3  A: 

The first version is correct, you need the mutex on both writes and reads.

However, AFAIK, almost all architectures out there have simple read and write accesses to one unit of data (eg. int) atomic. However, note that on architectures with weak memory ordering, you might have issues like seeing the "buffer-full" flag to true before the buffer actually contains the data.

Note that the code is probably not the best you can do, because it never sleeps (uses busy waiting). If you wanted to wait for the data in either thread, you would have to use a condition variable with the mutex.

jpalecek
Thanks for your answer!I think I don't fully understand the last paragraph. My 2 threads are all the time working, even though they may not executed the functions inside the "if" if the condition is not met. is it right to use sleep() even if they keep working anyway? I have never used it :|
alvatar
If your threads do some work all the time, and never have to wait for the other thread to fill/empty the buffer (as could eg. the drawing code in a game), you can just ignore the part about sleeping. Also, using sleep() is almost always wrong even if you actually need to sleep,
jpalecek
Understood. Thanks :)
alvatar
+4  A: 

This is derived from your first version - it is somewhat simpler.

Thread 1: writer

pthread_mutex_lock(&lattice_mutex);
if (!newData) {
    pthread_mutex_unlock(&lattice_mutex);  // Omit?
    uchar *lattice_pos = lattice;
    int i;
    for (i = 0; i < size; i++)
        *lattice_pos++ = rand() % CHAR_MAX;
    pthread_mutex_lock(&lattice_mutex);   // Omit?
    newData = TRUE;
}
pthread_mutex_unlock(&lattice_mutex);

Thread 2: reader

pthread_mutex_lock(&lattice_mutex);
if (newData) {
    pthread_mutex_unlock(&lattice_mutex);   // Omit?
    renderUpdate();
    pthread_mutex_lock(&lattice_mutex);     // Omit?
    newData = FALSE;
}
pthread_mutex_unlock(&lattice_mutex);

It depends on exactly how the lattice information is meant to be used, but given the name of the mutex, I think you should keep it locked while you are modifying the lattice, hence the two pairs of lines marked 'Omit?' should be removed. Otherwise, the lattice is not protected from concurrent access.

Added: I think the second version is erroneous - it doesn't properly protect the lattice.

Jonathan Leffler
Thanks! It is actually simpler... :)
alvatar
@Alvaro: are you saying that this was an accurate assessment?And that the lines marked 'Omit?' could be omitted?
Jonathan Leffler
They can be ommited. Actually is the simplest and best solution. However, I had to use the newData variable, otherwise performance is really bad. I don't know exactly why.
alvatar