Implement a Lock Acquire and Release in C++


Implementing a Spinlock Using the Atomic TAS Instruction

Implementing a Spinlock Using the Atomic TAS Instruction
Building a Spinlock from Scratch with Atomic TAS
Implementing a Minimal Mutex Using Test-and-Set
Inside Spinlocks: TAS, Atomicity, and Busy Waiting
Atomic Operations and Spinlocks: Thread Synchronization in C
From Atomic Instructions to Locks: A Complete Guide to TAS and Spinlocks
Hands-On Spinlock Implementation: tryLock, lockAcquire, and lockRelease
Your First Spinlock: A TAS-Based Implementation in C
Atomic Exchange and Thread Mutual Exclusion: A Guide to Implementing Spinlocks

Suppose we are given a TAS (Take-And-Set) function. This operation returns the old value stored in memory and atomically replaces it with a new value. Being atomic means that no other thread can observe the intermediate state; the entire read–write pair happens as one indivisible step.

In C++, the standard library function std::exchange behaves the same way logically, but is not atomic. Hardware-level atomicity is required for synchronization primitives.

int TAS(int* memory, int newVal) {
    int old = *memory;
    *memory = newVal;
    return old;
}

We want to use this primitive to implement a simple spinlock with:

  • lockAcquire()
  • lockRelease()

Threads will call these functions to protect access to a shared variable:

typedef struct {
    int lock;
} lockType;

typedef struct {
    int val;
} threadArgType;

void threadFunc(void* arg) {
    lockAcquire((static_cast<lockType*>arg)->lock);
    (static_cast<threadArgType*>arg)->val++;
    lockRelease((static_cast<lockType*>arg)->lock);
}

Implementing tryLock

A tryLock function attempts to acquire the lock once. If the lock was free (value = 0), TAS sets it to 1 and returns the old value (0). If the lock was already taken, TAS returns 1. The tryLock function is not blocking – it returns immediately.

So tryLock() should succeed only when TAS returns 0:

enum {
    UNLOCKED = 0,
    LOCKED = 1
}

int tryLock(lockType* lock) {
    // Returns 1 if previously locked, 0 if previously unlocked
    int old = TAS(lock->lock, LOCKED);
    return (old == UNLOCKED);   // true (1) = acquired successfully
}

Implementing lockAcquire()

A normal lock acquisition should “spin” until tryLock() succeeds. This is called a spinlock because the CPU will busy-wait. We can introduce a brief sleep when necessary. For instance, sleep(0) doesn’t actually pause execution but yields the CPU, allowing other threads to run.

It is commonly used to implement spin locks for mutual exclusion across threads.

void lockAcquire(lockType* lock) {
    while (!tryLock(lockType* lock)) {
        // spin until lock becomes available
    }
}

another implementation:

void lockAcquire(lockType* lock) {
    do {
       if (tryLock(lockType* lock)) {
          break;
       }
    } while (1);
}

Unrolling tryLock:

void lockAcquire(lockType* lock) {
    do {
       int old = TAS(lock->lock, LOCKED);
       // the lock has been set to LOCKED no matter if a lock has been aquired
       if (old == UNLOCKED) {
           break;
       }
    } while (1);
}

This is the simplest possible implementation using TAS. In real systems we may add pause instructions or backoff strategies, but the basic idea remains the same.

Implementing lockRelease()

To release the lock, the holder simply writes 0 to the lock variable. Because TAS is “set to new value and return old value”, it works fine for releasing too:

void lockRelease(lockType* lock) {
    TAS(lock->lock, UNLOCKED);
}

Alternatively, a simple atomic store is enough, but since TAS is our only tool, we reuse it. Keep in mind that releasing the lock twice is harmless here, since setting it to UNLOCKED=0 again has no side effects.

Summary

Using only an atomic TAS instruction, we implemented:

  • A tryLock() attempt
  • A lockAcquire() spinlock
  • A lockRelease() unlock operation

This style of lock is foundational for understanding low-level concurrency, memory ordering, and how higher-level mutex libraries are built.

C/C++ Programming

–EOF (The Ultimate Computing & Technology Blog) —

758 words
Last Post: Cambridge Science Park: Microsoft, AMD and Raspberry Pi (Neighbours)
Next Post: Introduction to Parquet Files, Read/Write using Python

The Permanent URL is: Implement a Lock Acquire and Release in C++ (AMP Version)

Leave a Reply