Thread: Mutex with atomics operations

  1. #1
    Registered User
    Join Date
    Mar 2020
    Posts
    8

    Mutex with atomics operations

    I want to build a low level mutex with atomic operations but I'm not sure if it's correct. It seems to work but I'm not sure with the memory order of atomic_exchange and atomic_store. Could you help me?

    This is the code:
    Code:
    #include <stdatomic.h>
    
    typedef volatile atomic_int TMutex;
    
    inline void MutexLock(TMutex *mutex)
    {
       while(atomic_exchange(mutex,1));
    }
    
    inline void MutexUnLock(TMutex *mutex)
    {
       atomic_store(mutex,0);
    }

  2. #2
    Registered User
    Join Date
    Dec 2017
    Posts
    1,084
    It looks reasonable.

    I don't think TMutex needs to be volatile. It isn't going to be changed by anything outside of the program, and it doesn't need to be volatile just because the function accepts a volatile.

    And the functions don't need the 'inline' keyword (which is "just a suggestion" in C, anyway). If you compile with optimization, the functions will be inlined.

    Have you tested it? Create two threads that increment a variable a hundred million times each, protected by a TMutex, and see if the result is two hundred million.
    The best argument against democracy is a five minute conversation with the average voter. - Churchill

  3. #3
    Registered User
    Join Date
    Mar 2020
    Posts
    8
    Thank you for the reply. The code works but I'm not sure about the memory order of atomic_exchange and atomic_store that is memory_order_seq_cst. Is it correct for this kind of operations?
    With atomic_store_explicit and atomic_exchange_explicit I can use a different one but I don't fully understand the differences between the memory orders so I'm not sure if it is correct.

  4. #4
    Registered User
    Join Date
    Dec 2017
    Posts
    1,084
    The code works but I'm not sure about the memory order of atomic_exchange and atomic_store that is memory_order_seq_cst. Is it correct for this kind of operations?
    "Sequentially consistent" memory ordering is the most constrained and therefore as "correct" as it gets. It's possible that you could get a performance boost with "relaxed" ordering, but unless you know exactly what that means it's best to stick with the default.

    If you want to read up on it (these don't necessarily all use the same terminology) :
    memory_order - cppreference.com
    Atomic/GCCMM/AtomicSync - GCC Wiki
    https://www.kernel.org/doc/Documenta...y-barriers.txt
    https://www.hpl.hp.com/techreports/C...C/WRL-95-7.pdf

    As far as I can tell, if you want the code to be portable then you shouldn't use volatile.
    (The last section in the first reference above discusses that.)
    Also, you might want to use an atomic_flag instead of an atomic_int.
    atomic_flag_test_and_set, atomic_flag_test_and_set_explicit - cppreference.com
    The best argument against democracy is a five minute conversation with the average voter. - Churchill

  5. #5
    Registered User
    Join Date
    Mar 2020
    Posts
    8
    Thank you very much for your complete answer. So for now I have:

    Code:
    typedef atomic_bool TMutex;
    
    void MutexLock(TMutex *mutex)
    {
       while(atomic_flag_test_and_set(mutex));
    }
    
    void MutexUnLock(TMutex *mutex)
    {
       atomic_flag_clear(mutex);
    }
    I will study the memory orders to further improve the performance.
    This code is already faster than a pthread mutex.

  6. #6
    Registered User
    Join Date
    Dec 2017
    Posts
    1,084
    This code is already faster than a pthread mutex.
    The pthread mutex system is much more complicated and doesn't just spin if it can't aquire a mutex. Instead it (eventually) sleeps. So it depends on the behavior you want. Sleeping allows more efficient use of the processor cores during a "long" wait. But if you're certain it will only ever need to wait for a very short time then spinning is probably okay. Wikipedia: Spinlock

    I'm not sure what OS you're on, but a linux version of the pthread code can be found here; pthreads. Look into pthread_mutex_lock.c. Note it's complexity! Also note the eventual futex_wait call, which is a "fast user-space locking" call. You might also find pthread_spin_lock.c interesting!

    I think atomic_flag is supposed to be used more like this. It is different from an atomic_bool.
    Code:
    #include <stdatomic.h>
     
    atomic_flag flag = ATOMIC_FLAG_INIT; // inits it to a cleared state
     
    void MutexLock(atomic_flag *mutex)
    {
       while(atomic_flag_test_and_set(mutex));
    }
     
    void MutexUnLock(atomic_flag *mutex)
    {
       atomic_flag_clear(mutex);
    }
    Here's a similar implementation, except that he used volatile (which, as I said, I'm not so sure about for portability) : Implementing a Mutex * GitHub
    The best argument against democracy is a five minute conversation with the average voter. - Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How are atomics done in C99 (portably)?
    By mid in forum C Programming
    Replies: 3
    Last Post: 04-02-2020, 09:30 PM
  2. Mutex
    By SuchtyTV in forum C Programming
    Replies: 7
    Last Post: 07-16-2019, 09:32 AM
  3. doing floating operations using integer operations
    By ammalik in forum C Programming
    Replies: 10
    Last Post: 08-15-2006, 04:30 AM
  4. Mutex in C
    By jgs in forum Windows Programming
    Replies: 4
    Last Post: 05-21-2005, 07:11 AM
  5. Mutex
    By DutchStud in forum Windows Programming
    Replies: 2
    Last Post: 11-06-2001, 12:09 AM

Tags for this Thread