Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do an atomic increment and fetch in C?

I'm looking for a way to atomically increment a short, and then return that value. I need to do this both in kernel mode and in user mode, so it's in C, under Linux, on Intel 32bit architecture. Unfortunately, due to speed requirements, a mutex lock isn't going to be a good option.

Is there any other way to do this? At this point, it seems like the only option available is to inline some assembly. If that's the case, could someone point me towards the appropriate instructions?

like image 794
Bryan Avatar asked Mar 01 '10 01:03

Bryan


People also ask

Is ++ atomic in C?

On objects without an atomic type, standard never defines ++ as an atomic operation.

Is increment Atomic C?

@ Steve: No, in C, increment is not atomic. If you need an atomic operation, use your platform's corresponding API. Relying on implementation-defined behaviour (which might change with compiler options or version without further notice) is the road to pain.

Is increment operation an atomic?

The increment-memory machine instruction on an X86 is atomic only if you use it with a LOCK prefix. x++ in C and C++ doesn't have atomic behavior.

What is an atomic variable 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.


2 Answers

GCC __atomic_* built-ins

As of GCC 4.8, __sync built-ins have been deprecated in favor of the __atomic built-ins: https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/_005f_005fatomic-Builtins.html

They implement the C++ memory model, and std::atomic uses them internally.

The following POSIX threads example fails consistently with ++ on x86-64, and always works with _atomic_fetch_add.

main.c

#include <assert.h>
#include <pthread.h>
#include <stdlib.h>

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

int global = 0;

void* main_thread(void *arg) {
    int i;
    for (i = 0; i < NUM_ITERS; ++i) {
        __atomic_fetch_add(&global, 1, __ATOMIC_SEQ_CST);
        /* This fails consistently. */
        /*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); 
    assert(global == NUM_THREADS * NUM_ITERS);
    return EXIT_SUCCESS;
}

Compile and run:

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

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

Tested in Ubuntu 18.10, GCC 8.2.0, glibc 2.28.

C11 _Atomic

In 5.1, the above code works with:

_Atomic int global = 0;
global++;

And C11 threads.h was added in glibc 2.28, which allows you to create threads in pure ANSI C without POSIX, minimal runnable example: How do I start threads in plain C?


GCC supports atomic operations:

gcc atomics

like image 30
dicroce Avatar answered Nov 15 '22 12:11

dicroce