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 FAILor, 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):
- pcscdaemon.c: Fix data race for AraKiri variable
- readerfactory.h: fix data race with hLockId
- Fix race with dwEventStatus
And simplified the code by removing a mutex in Remove mutex and use _Atomic instead.