Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Friday, January 21, 2022

Multi-thread and Atomic

Multi-thread programming seams easy but it is difficult to write correct multi-threading code.

For example pcsc-lite and my CCID driver use threads and are not (yet) perfect. One problem in particular is the access to the same variable from different threads.

C11 standard defines the Atomic types to make multi-thread programming easier.

Source code

This source code exhibits the problem.

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

enum CONSTANTS {
    NUM_THREADS = 1000,
    NUM_ITERS = 1000
};

_Atomic int global_a = 0;
int global = 0;

static void* main_thread(void *arg)
{
    (void)arg;

    int i;
    for (i = 0; i < NUM_ITERS; ++i)
    {
        global_a++;
        global++;
    }
    return NULL;
}

int main(void)
{
    int i;
    pthread_t threads[NUM_THREADS];

    for (i = 0; i < NUM_THREADS; ++i)
        pthread_create(&threads[i], NULL, main_thread, NULL);
    for (i = 0; i < NUM_THREADS; ++i)
        pthread_join(threads[i], NULL);

    printf("global_a %d %s\n", global_a,
        global_a == NUM_THREADS * NUM_ITERS ? "OK" : "FAIL");

    printf("global   %d %s\n", global,
        global == NUM_THREADS * NUM_ITERS ? "OK" : "FAIL");

    return 0;
}

Result

If I compile and run the sample code I get:

global_a 1000000 OK
global   660409 FAIL
or, with another execution:
global_a 1000000 OK
global   691552 FAIL

You can see that the variable global that is NOT declared with _Atomic does not have the expected value. Some updates of the variable value failed (were skipped).

Another option if you do not want or can't use _Atomic is to use pthread_mutex_lock() and pthread_mutex_unlock() to protect the accesses to the variable. But the code is then harder to read.

Impact on pcsc-lite and libccid

The problem was reported by andrei-datcu in the pull request No data races in EHStatusHandlerThread.

I then fixed different problems in these changes (non-exhautive list):

And simplified the code by removing a mutex in Remove mutex and use _Atomic instead.

Conclusion

The next versions of pcsc-lite and libccid will be safer and more correct.