UNIX Portable Atomic Operations

37,806

Solution 1

As of C11 there is an optional Atomic library which provides atomic operations. This is portable to whatever platform that has a C11 compiler (like gcc-4.9) with this optional feature.

The presence of the atomic can be checked with __STDC_NO_ATOMICS__and the presence of <stdatomic.h>

atomic.c

#include <stdio.h>
#include <stdlib.h>
#ifndef __STDC_NO_ATOMICS__
#include <stdatomic.h>
#endif

int main(int argc, char**argv) {
    _Atomic int a;
    atomic_init(&a, 42);
    atomic_store(&a, 5);
    int b = atomic_load(&a);
    printf("b = %i\n", b);

    return EXIT_SUCCESS;
}

Compiler invocations

clang -std=c11 atomic.c
gcc -std=c11 atomic.c

Solution 2

For anyone who stumbles upon this in the future, C11 atomics are the best way to do this now - I believe they will be included in GCC 4.9.

Solution 3

Since you asked for OS X:

(and since cross platformity was raised in this thread.)

OS X has functions OSAtomicAdd32() and friends. They are declared in "/usr/include/libkern/OSAtomic.h". See The Threading Programming guide, section "Using Atomic Operations".

And for Windows, there is InterlockedIncrement() and friends (see MSDN).

Together with the gcc builtins __sync_fetch_and_add() and friends (has been linked above), you should have something for every main desktop platform.

Please note that I did not yet use them by myself, but maybe will do so in the next few days.

Solution 4

No, POSIX does not specify any portable lock-free/atomic operations. That's why they have pthreads.

You're either going to have to use non-standard ways or stick with ptrheads for portability.

Solution 5

C11 atomics minimal runnable example

With the addition of threads in glibc 2.28, we can do both atomics and threading in pure C11.

Example 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)
{
    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 -std=c11 main.c -pthread
./a.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.

A pthreads example can be found at: How do I start threads in plain C?

Tested in Ubuntu 18.04 (glibc 2.27) by compiling glibc from source: Multiple glibc libraries on a single host Ubuntu 18.10 has glibc 2.28 so things should just work there.

Share:
37,806
dmeister
Author by

dmeister

Software engineer, storage researcher: JavaPythonScalaErlangC/C++C# UnixLinux KernelStorageFilesystems....

Updated on May 22, 2020

Comments

  • dmeister
    dmeister almost 4 years

    Is there a (POSIX-)portable way in C for atomic variable operations similar to a portable threading with pthread?

    Atomic operations are operations like "increment and get" that are executed atomically that means that no context switch can interfere with the operation. In Linux kernel space, we have to atomic_t type, in Java we have the java.util.concurrent.atomic package.

    On Linux, the atomic.h file provides atomic operations, but the include is platform dependent e.g. #include <asm-x86_64/atomic.h> and it is not available on Mac OS X in a similar way.