Search code examples
multithreadingmemorymutexread-write

Concurrent read write


Below is my code in which two variables are updated upon callback which is called every 50ms. There is also a reader thread that just wakes up every 50ms and reads the variables.

After going through this, I guessed that there will be some instances when the thread that reads will wake up just when a callback is received and since i am not locking the same mutex while reading and writing, in this case it will result in inconsistent memory being read.

However, when i run it, this scenario is never occurring. Is it that I have not run it for long enough or is there any mistake in my understanding?

recursive_mutex mutex1
recursive_mutex mutex2
var1, var2

//called every 50ms
onUpdateListener() {
    lock(mutex1)
    update var1
    update var2
}

VarReaderThread::readVar() {
    sleep(50)
    while(true) {
        {
        lock(mutex2)
        read var1
        read var2
        sleep(50)
        }
   }
}

Solution

  • A data race occurs when two or more threads are accessing shared data where at least one of the threads performs a write operation such that one or more threads may encounter an inconsistent state.

    The code provided has two mutexes one for read and one for write. That will in no way exclude a thread from reading the shared data (var1,var2 in the example) while another is writing to them or more precisely after one has started writing to one of them and before completing writing to the other in a situation where that intermediate state would be considered an inconsistent state within the parameters of the code logic and its purpose.

    One mutex is required to make reading and writing exclusive. As an aside, it's not clear why a recursive mutex has been declared. That would only be required if a given thread may encounter code requiring it to obtain a mutex it already holds. It's better design to design and code that situation out where possible.

    Explicit unlock steps have been introduced which may or may not be required explicitly depending on the coding model (required in C, use std::lock_guard in C++ or synchronized or finally in Java, etc.).

    Some condition has been introduced to indicate towards a proper termination condition. while(true) is bad practice unless some other condition will terminate it gracefully.

    More sophisticated models may use a 'shared mutex' in which more than one thread may hold it in 'read' mode but only one if one holds it in 'write' mode. That may or may not require some signaling to make sure 'write' doesn't get into a live-lock where many readers endlessly block 'write' access.

    mutex mutex_v1_v2 //The mutex controlling shared state var1, var2.
    var1, var2
    
    //called every 50ms
    onUpdateListener() {
        lock(mutex_v1_v2)
        update var1
        update var2
        unlock(mutex_v1_v2)
    }
    
    VarReaderThread::readVar() {
        sleep(50)
        while(!finished) {
            lock(mutex_v1_v2)
            read var1
            read var2
            unlock(mutex_v1_v2)
            sleep(50) //V. V. important to sleep not holding the mutex!
       }
    }