I need to use an atomic variable in C as this variable is accessed across different threads. Don't want a race condition. My code is running on CentOS. What are my options?
374k 114 114 gold badges 1.3k 1.3k silver badges 1k 1k bronze badges asked Aug 15, 2014 at 1:00 1,957 4 4 gold badges 27 27 silver badges 63 63 bronze badges Commented Aug 15, 2014 at 1:02It looks like pthreads for CentOS are available, so you could use mutex to synchronize access to things.
Commented Aug 15, 2014 at 1:08 The type volatile sig_atomic_t is guaranteed to have atomic access Commented Aug 15, 2014 at 1:58@RickyMutschlechner: volatile DOES NOT DO THAT. Please refer to the Wikipedia article you linked to: "Operations on volatile variables are not atomic. "
Commented Aug 15, 2014 at 2:01@RickyMutschlechner: You can't implement a spin-lock just using volatile , because the processor will reorder IO even if the compiler doesn't. The question also says "don't want a race condition", and volatile just isn't the right tool for the job, if you are concerned about race conditions.
Commented Aug 15, 2014 at 3:41C11 atomic primitives
_Atomic const int * p1; // p is a pointer to an atomic const int const atomic_int * p2; // same const _Atomic(int) * p3; // same
Added in glibc 2.28. Tested in Ubuntu 18.04 (glibc 2.27) by compiling glibc from source: Multiple glibc libraries on a single host Later also tested on Ubuntu 20.04, glibc 2.31.
#include #include #include atomic_int acnt; int cnt; int f(void* thr_data) < (void)thr_data; for(int n = 0; n < 1000; ++n) < ++cnt; ++acnt; // for this example, relaxed memory order is sufficient, e.g. // atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed); >return 0; > int main(void)
Compile and run:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread ./main.out
The atomic counter is 10000 The non-atomic counter is 8644
The non-atomic counter is very likely to be smaller than the atomic one due to racy access across threads to the non atomic variable.
answered May 16, 2017 at 6:20 Ciro Santilli OurBigBook.com Ciro Santilli OurBigBook.com 374k 114 114 gold badges 1.3k 1.3k silver badges 1k 1k bronze badgesIf you are using GCC on your CentOS platform, then you can use the __atomic built-in functions.
Of particular interest might be this function:
— Built-in Function: bool __atomic_always_lock_free (size_t size, void *ptr)
This built-in function returns true if objects of size bytes always generate lock free atomic instructions for the target architecture. size must resolve to a compile-time constant and the result also resolves to a compile-time constant.ptr is an optional pointer to the object that may be used to determine alignment. A value of 0 indicates typical alignment should be used. The compiler may also ignore this parameter.
if (_atomic_always_lock_free (sizeof (long long), 0))
answered Aug 15, 2014 at 1:58
70.1k 8 8 gold badges 113 113 silver badges 195 195 bronze badges
I am going to toss in my two cents in case someone benefits. Atomic operations are a major problem in Linux. I used gatomic.h at one time only to find it gone. I see all kinds of different atomic options of either questionable reliability or availability -- and I see things changing all the time. They can be complex with tests needed by O/S level, processor, whatever. You can use a mutex -- not only complex by dreadfully slow.
Although perhaps not ideal in threads, this works great for atomic operations on shared memory variables. It is simple and it works on every O/S and processor and configuration known to man (or woman), dead reliable, easy to code, and will always work.
Any code can me made atomic with a simple primitive -- a semaphore. It is something that is true/false, 1/0, yes/no, locked/unlocked -- binary.
Once you establish the semaphore:
set semaphore //must be atomic
do all the code you like which will be atomic as the semaphore will block for you
release semaphore //must be atomic
Relatively straight forward except the "must be atomic" lines.
It turns out that you easily assign your semaphores a number (I use a define so they have a name like "#define OPEN_SEM 1" and "#define "CLASS_SEM 2" and so forth.
Find out your largest number and when your program initializes open a file in some directory (I use one just for this purpose). If not there create it:
if (ablockfd < 0) < //ablockfd is static in case you want to //call it over and over char *get_sy_path(); char lockname[100]; strcpy(lockname, get_sy_path()); strcat(lockname, "/metlock"); ablockfd = open(lockname, O_RDWR); //error code if ablockfd bad >
Now to gain a semaphore:
Now use your semaphore number to "lock" a "record" in your file of length one byte. Note -- the file will never actually occupy disk space and no disk operation occurs.
//sem_id is passed in and is set from OPEN_SEM or CLASS_SEM or whatever you call your semaphores. lseek(ablockfd, sem_id, SEEK_SET); //seeks to the bytes in file of //your semaphore number result = lockf(ablockfd, F_LOCK, 1); if (result != -1) < //got the semaphore >else < //failed >
To test if the semaphore is held:
result = lockf(ablockfd, F_TEST, 1); //after same lseek
To release the semaphore:
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek
And all the other things you can do with lockf -- blocking/non-blocking, etc.
Note -- this is WAY faster than a mutex, it goes away if the process dies (a good thing), simple to code, and I know of no operating system with any processor with any number of them or number of cores that cannot atomically lock a record . so simple code that just works. The file never really exists (no bytes but in directory), seems to be no practical limit to how many you may have. I have used this for years on machines with no easy atomic solutions.