Why atom_t user space in gcc (3.4.5) doesn't work

Recently, in my work, I want to implement a counter in a multi-threaded program. I found that in my GCC (3.4.5) there is a user space data type called atomic_t . But this, apparently, is not atomic.

I tested atomic_inc () / atomic_read () on an x86_64 machine with 12 cores, and the linux kernel is 2.6.9.

This is a demonstration. I add pthread_cond_t and pthread_cond_broadcast to increase the degree of concurrency.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdint.h> #include <pthread.h> #include <asm/atomic.h> atomic_t atomic_count; pthread_cond_t g_cond; pthread_mutex_t g_mutex; void* thread_func(void*p) { pthread_mutex_t* lock = (pthread_mutex_t*)p; pthread_mutex_lock(lock); pthread_cond_wait(&g_cond, lock); pthread_mutex_unlock(lock); for (int i=0;i<20000;++i){ atomic_inc(&atomic_count); } return NULL; } #define THRD_NUM 15 int main() { atomic_set(&atomic_count, 0); pthread_cond_init(&g_cond, NULL); pthread_mutex_init(&g_mutex, NULL); pthread_t pid[THRD_NUM]; for (int i=0; i<THRD_NUM; i++) { pthread_create(&pid[i], NULL, thread_func, &g_mutex); } sleep(3); pthread_cond_broadcast(&g_cond); for (int i=0; i<THRD_NUM; i++) { pthread_join(pid[i], NULL); } long ans = atomic_read(&atomic_count); printf("atomic_count:%ld \n", ans); } 

The expected result is 300,000, but we always get 270,000+ or ​​280,000+ instead. I found an implementation of atomic_inc ()

  static __inline__ void atomic_inc(atomic_t *v) { __asm__ __volatile__( LOCK "incl %0" :"=m" (v->counter) :"m" (v->counter)); } 

According to Intel, the LOCK prefix has the semantics of a complete barrier. Does this mean that the program output has nothing to do with the reordering of commands?

Moreover, I found an interesting phenomenon. If I set THRD_NUM to less than 12 (my machine core number), the error will be less. I think this could be caused by context switching. But I have no idea how this happened. Can anybody help me? Thanks!

+4
source share
3 answers

LOCK is a macro. Are you sure that this is actually defined as a "lock", as it should be in reality?

I am sure that you use headers only for the kernel. Material in "asm" should not be used in the user area.

+2
source

I assume that <asm/atomic.h> is some kind of Linux kernel header? This, of course, is not part of C. In C11, there is an atomic through which you pass <stdatomic.h> . You will need to switch to the new gcc.

I don’t know where you get the LOCK macro from, but I don’t think it is defined correctly. In the Linux version of asm / atomic.h, which I found on a Google search , they use a macro called LOCK_PREFIX , not LOCK .

Here is their code for atom_inc:

 93 static inline void atomic_inc(atomic_t *v) 94 { 95 asm volatile(LOCK_PREFIX "incl %0" 96 : "+m" (v->counter)); 97 } 

It should just turn into:

 asm volatile("lock incl %0" : "+m" (v->counter)); 

I would stop worrying about macros and just enter a line in ( "lock incl %0" ).

Then compile gcc -S myfile.c and make sure that in myfile.s you see the lock incl statement, not just incl (without LOCK ).

0
source

I believe

 atomic_t atomic_count; 

Must be

 volatile atomic_t atomic_count; 

Try it.

-2
source

Source: https://habr.com/ru/post/1493463/


All Articles