Thread C11 + C11 atomic_int
Added in glibc 2.28. Tested on Ubuntu 18.10 amd64 (ships with glic 2.28) and Ubuntu 18.04 (ships with glibc 2.27) by compiling glibc 2.28 from source: Multiple glibc libraries on the same host
Example adapted from: https://en.cppreference.com/w/c/language/atomic
main.c
#include <stdio.h> #include <threads.h> #include <stdatomic.h> atomic_int atomic_counter; int non_atomic_counter; int mythread(void* thr_data) { (void)thr_data; for(int n = 0; n < 1000; ++n) { ++non_atomic_counter; ++atomic_counter; // for this example, relaxed memory order is sufficient, eg // atomic_fetch_add_explicit(&atomic_counter, 1, memory_order_relaxed); } return 0; } int main(void) { thrd_t thr[10]; for(int n = 0; n < 10; ++n) thrd_create(&thr[n], mythread, NULL); for(int n = 0; n < 10; ++n) thrd_join(thr[n], NULL); printf("atomic %d\n", atomic_counter); printf("non-atomic %d\n", non_atomic_counter); }
Github upstream .
Compile and run:
gcc -ggdb3 -std=c11 -Wall -Wextra -pedantic -o main.out main.c -pthread ./main.out
Possible conclusion:
atomic 10000 non-atomic 4341
Most likely, the non-atomic counter will be smaller than the atomic counter due to quick access through flows to the non-atomic variable.
See also: How to make atomic increment and extract in C?
Disassembly analysis
Disassemble with:
gdb -batch -ex "disassemble/rs mythread" main.out
contains:
17 ++non_atomic_counter; 0x00000000004007e8 <+8>: 83 05 65 08 20 00 01 addl $0x1,0x200865(%rip) # 0x601054 <non_atomic_counter> 18 __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST); 0x00000000004007ef <+15>: f0 83 05 61 08 20 00 01 lock addl $0x1,0x200861(%rip) # 0x601058 <atomic_counter>
therefore, we see that the atomic increment is executed at the instruction level with the lock prefix f0 .
With aarch64-linux-gnu-gcc 8.2.0 we get instead:
11 ++non_atomic_counter; 0x0000000000000a28 <+24>: 60 00 40 b9 ldr w0, [x3] 0x0000000000000a2c <+28>: 00 04 00 11 add w0, w0, #0x1 0x0000000000000a30 <+32>: 60 00 00 b9 str w0, [x3] 12 ++atomic_counter; 0x0000000000000a34 <+36>: 40 fc 5f 88 ldaxr w0, [x2] 0x0000000000000a38 <+40>: 00 04 00 11 add w0, w0, #0x1 0x0000000000000a3c <+44>: 40 fc 04 88 stlxr w4, w0, [x2] 0x0000000000000a40 <+48>: a4 ff ff 35 cbnz w4, 0xa34 <mythread+36>
therefore, the atomic version actually has a cbnz loop that runs until the stlxr repository is successful.
Benchmark
DO. Create a benchmark to show that atomic is slower.
POSIX Themes
main.c
#define _XOPEN_SOURCE 700 #include <assert.h> #include <stdlib.h> #include <pthread.h> enum CONSTANTS { NUM_THREADS = 1000, NUM_ITERS = 1000 }; int global = 0; int fail = 0; pthread_mutex_t main_thread_mutex = PTHREAD_MUTEX_INITIALIZER; void* main_thread(void *arg) { int i; for (i = 0; i < NUM_ITERS; ++i) { if (!fail) pthread_mutex_lock(&main_thread_mutex); global++; if (!fail) pthread_mutex_unlock(&main_thread_mutex); } return NULL; } int main(int argc, char **argv) { pthread_t threads[NUM_THREADS]; int i; fail = argc > 1; 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 ./main.out 1
The first run works fine, the second fails due to lack of synchronization.
POSIX Standardized Atomic Operations Does Not Appear: Portable UNIX Atomic Operations
Tested on Ubuntu 18.04. Github upstream .
GCC __atomic_* Built-in Modules
For those who do not have C11, you can achieve atomic increments with __atomic_* GCC extensions.
main.c
#define _XOPEN_SOURCE 700 #include <pthread.h> #include <stdatomic.h> #include <stdio.h> #include <stdlib.h> enum Constants { NUM_THREADS = 1000, }; int atomic_counter; int non_atomic_counter; void* mythread(void *arg) { (void)arg; for (int n = 0; n < 1000; ++n) { ++non_atomic_counter; __atomic_fetch_add(&atomic_counter, 1, __ATOMIC_SEQ_CST); } return NULL; } int main(void) { int i; pthread_t threads[NUM_THREADS]; for (i = 0; i < NUM_THREADS; ++i) pthread_create(&threads[i], NULL, mythread, NULL); for (i = 0; i < NUM_THREADS; ++i) pthread_join(threads[i], NULL); printf("atomic %d\n", atomic_counter); printf("non-atomic %d\n", non_atomic_counter); }
Compile and run:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread ./main.out
Output and generated assembly: the same as in the example with "C11 threads".
Tested on Ubuntu 16.04 amd64, GCC 6.4.0.