18

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?

2 Answers 2

24

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?

Sign up to request clarification or add additional context in comments.

Comments

8

GCC supports atomic operations:

gcc atomics

4 Comments

In particular, __sync_add_and_fetch sounds like what the OP is after.
I found those before, but there are two problems. The first is that my understanding is they won't work with the kernel compiler... I'm not sure why that is, so maybe there's an easy work around. Second, even in userspace, the linker gives me the following error: undefined reference to `__sync_add_and_fetch_2' If someone can point out solutions to either of those problems (or both!) I'd be very appreciative.
Just as an update, __sync_add_and_fetch does work in userspace, if you include the -march=pentium flag when compiling (stackoverflow.com/questions/130740/…), so now I'm just working on getting that working in the kernel module.
Another update, I believe this is because the function expects an int, whereas I'm using (and constrained to) a short.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.