17

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Bryan
  • 2,068
  • 2
  • 15
  • 18

2 Answers2

21

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
8

GCC supports atomic operations:

gcc atomics

dicroce
  • 45,396
  • 28
  • 101
  • 140
  • 1
    In particular, `__sync_add_and_fetch` sounds like what the OP is after. – caf Mar 01 '10 at 01:30
  • 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. – Bryan Mar 01 '10 at 02:31
  • Just as an update, __sync_add_and_fetch does work in userspace, if you include the -march=pentium flag when compiling (http://stackoverflow.com/questions/130740/link-error-when-compiling-gcc-atomic-operation-in-32-bit-mode), so now I'm just working on getting that working in the kernel module. – Bryan Mar 01 '10 at 02:49
  • Another update, I believe this is because the function expects an int, whereas I'm using (and constrained to) a short. – Bryan Mar 01 '10 at 02:54