I recently had an interview where I was asked to show how to prevent race conditions in C or C++ between two threads operating on shared data. I used a mutex as follows :

pthread_mutex_t mutex;
int shared_var = 0;

void thread1()
{
    pthread_mutex_lock(&mutex); // Lock the mutex
    if (shared_var == 3)
    {
        // do something, then decrement shared_var 
        shared_var--;
    }
    pthread_mutex_unlock(&mutex); // Unlock the mutex
}

void thread2()
{
    pthread_mutex_lock(&mutex); // Lock the mutex
    shared_var++;
    pthread_mutex_unlock(&mutex); // Unlock the mutex
}

Then I was asked to do this in a lockless way using atomic variables instead of using a mutex.

I got stuck here a bit. Spinlocks aside, Is there a way to do this by just having shared_var an atomic variable?

At the hardware level, I guess atomic operations would use something like a compare and swap operation. If thread1 detects that shared_var == 3, but then thread2 updates shared_var by time thread1 goes to decrement it, what will happen? Will the decrement fail because thread1 detects that shared_var has since been updated? Then what? I'm generally confused as to how to implement lockless operations.