Search code examples
multithreadingx86operating-systemlockingmutual-exclusion

Using fetch-and-add as lock


I am trying to understand how fetch-and-add can be used as a lock. Here is what the book (OS's: 3 Easy pieces) says:

The basic operation is pretty simple: when a thread wishes to acquire a lock, it first does an atomic fetch-and-add on the ticket value; that value is now considered this thread’s “turn” (myturn). The globally shared lock->turn is then used to determine which thread’s turn it is; when (myturn == turn) for a given thread, it is that thread’s turn to enter the critical section.

What I do not understand is how the thread checks if the lock held by another process before entering the cretical seection. All I can read that the value will be incremented, no mention of checks!

Another part says:

Unlock is accomplished simply by incrementing the turn such that the next waiting thread (if there is one) can now enter the critical section.

Which I can not interpret in a way where checks will not be performed, which can not be true becuase it compremises the whole porpose of locking cretical sections. What am I fmissing here? Thanks.


Solution

  • What I do not understand is how the thread checks if the lock held by another process before entering the cretical seection.

    You need an "atomic fetch" for this, maybe something like "while( atomic_fetch(currently_serving) != my_ticket) { /* wait */ }".

    If you have "atomic fetch and add", then you can implement "atomic fetch" by doing "atomic fetch and add the value zero", maybe something like "while( atomic_fetch_and_add(currently_serving, 0) != my_ticket) { /* wait */ }".

    For reference; the full sequence could be something like:

        my_ticket = atomic_fetch_and_add(ticket_counter, 1);
    
        while( atomic_fetch_and_add(currently_serving, 0) != my_ticket) {
            /* wait */
        }
    
        /* Critical section (lock successfully acquired). */
    
        atomic_fetch_and_add(currently_serving, 1);   /* Release the lock */
    

    Of course you might have a better atomic fetch you can use instead (e.g. for some CPUs any normal aligned load is atomic).