Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use atomic variables in C?

Tags:

c

linux

atomic

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?

like image 254
lppier Avatar asked Aug 15 '14 01:08

lppier


People also ask

What is an atomic variable in C programming?

In C, _Atomic is used as a type specifier. It is used to avoid the race condition if more than one thread attempts to update a variable simultaneously. It is defined in the stdatomic.

How do atomic operations work in C?

Atomics as part of the C language are an optional feature that is available since C11. Their purpose is to ensure race-free access to variables that are shared between different threads. Without atomic qualification, the state of a shared variable would be undefined if two threads access it concurrently.

How do atomic variables work?

The atomic variable allows us to perform an atomic operation on a variable. Atomic variables minimize synchronization and help avoid memory consistency errors. Hence, it ensures synchronization.

Is ++ atomic in C?

On objects without an atomic type, standard never defines ++ as an atomic operation. C11 defines atomic types in stdatomic. h. If you have an object with an atomic type, a postfix and prefix operators ++ will define an atomic operation as: read-modify-write operation with memory_order_seq_cst memory order semantics.


1 Answers

C11 atomic primitives

http://en.cppreference.com/w/c/language/atomic

_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.

Example adapted from: https://en.cppreference.com/w/c/language/atomic

main.c

#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>

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)
{
    thrd_t thr[10];
    for(int n = 0; n < 10; ++n)
        thrd_create(&thr[n], f, NULL);
    for(int n = 0; n < 10; ++n)
        thrd_join(thr[n], NULL);

    printf("The atomic counter is %u\n", acnt);
    printf("The non-atomic counter is %u\n", cnt);
}

Compile and run:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out

Possible output:

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.

Disassembly analysis at: How do I start threads in plain C?