1. 7

Glibc/musl already do this for you, but this may still be useful on other platforms!

  1.  

  2. 3

    This looks racy to me, can someone explain where I’m going wrong?

    Thread A is the first to acquire the benephore, checks and increments the atomic variable, finds it is zero and proceeds without acquiring the semaphore.

    While Thread A is in the critical section Thread B arrives and acquires the benephore, finds the atomic variable to be non-zero so acquires the semaphore. Because it has the semaphore it proceeds into the critical section. Now there are two threads concurrently in the critical section.

    What prevents this scenario?

    1. 3

      I think you’re right, unless I’m missing something obvious.

      Worse still, if T1 enters the critical section and is followed by T2, if T1 now makes progress it will find benaphore_atom > 1 and call release_sem() on a semaphore it doesn’t hold. Which is probably either a crash or UB, who knows.

      I was missing something obvious.

      The semaphore, is initialized into an ‘unavailable’ state.

      When Thread B attempts to acquire the newly initialized semaphore, it blocks as the semaphore is in its ‘unavailable’ state. Thread A later finishes up in its Critical Section, and seeing that benaphore_atom > 1 it increments the semaphore, allowing Thread B to make progress.

      At the end of this execution, T2 sees !(benaphore_atom > 1) and continues without marking the semaphore as available.

      1. 1

        Semaphores don’t provide mutual exclusion.

        You use them to e.g. count the number of items in a queue and wake up enough threads to process them. Then those threads use a separate mutex to synchronise any operations on the queue.