C++11 spinlock

Apart from mutexes, spinlocks provide another way of protecting critical sections from multi-threaded access. Spinlocks perform the same job as mutexes but in a different way.

During mutex locking the OS may send the thread to sleep and wake it up when the lock can be obtained by this thread. Sending a thread to sleep and waking it are somewhat expensive operations but under some circumstances the OS may replace the waiting thread with another that has some meaningful work to perform. Spinlocks use another way of waiting called “busy waiting”. Instead of sending the thread to sleep, spinlocks “trap” the thread in a loop by constantly checking if the lock can be obtained. During busy waiting the thread is kept alive making it difficult for the OS to replace it with another one.

Mutexes are better for long locking periods where spinlocks for shorter. C++11 has build-in support for mutexes but not for spinlocks. Fortunately creating a spinlock class is quite easy.

#include <atomic>

class SpinLock
{
public:
    void lock()
    {
        while(lck.test_and_set(std::memory_order_acquire))
        {}
    }

    void unlock()
    {
        lck.clear(std::memory_order_release);
    }

private:
    std::atomic_flag lck = ATOMIC_FLAG_INIT;
};

And an example/benchmark of using a spinlock is the following:

SpinLock lock;
//std::mutex lock;
int count[8] = {0, 0, 0, 0, 0, 0, 0, 0};

void foo(int n)
{
    for(unsigned i = 0; i < 10000000; i++)
    {
        lock.lock();
        ++count[n];
        lock.unlock();
    }
}

int main(int, char**)
{
    std::vector<std::thread> v;
    for(int n = 0; n < 8; ++n)
    {
        v.emplace_back(foo, n);
    }

    for (auto&amp; t : v) 
    {
        t.join();
    }

    return count[0] + count[1] + count[2] + count[3]
        + count[4] + count[5] + count[6] + count[7];
}

Running the above benchmark using time command:

Mutex locking:
real 0m12.167s
user 0m7.760s
sys 1m20.169s

Spinlock locking:
real 0m10.925s
user 1m14.653s
sys 0m0.008s

It seems that for the above simplified scenario, the spinlock performs better and minimizes the time spend in the kernel.

That’s all, happy codding!