3

I wanted to know if boost has any libraries that assist in implementing spin locks. I know boost supports mutexes but I could not find any examples that show or describe spinlocks in boost.Any examples showing how to implement a spin lock using boost(preferably) would be appreciated.(C++98)

Rajeshwar
  • 11,179
  • 26
  • 86
  • 158

4 Answers4

6

Example using Boost.Atomic:

#include <boost/atomic.hpp>

class SpinLock
{
    boost::atomic_flag flag; // it differs from std::atomic_flag a bit -
                             // does not require ATOMIC_FLAG_INIT
public:
    void lock()
    {
        while( flag.test_and_set(boost::memory_order_acquire) )
            ;
    }
    bool try_lock()
    {
        return !flag.test_and_set(boost::memory_order_acquire);
    }
    void unlock()
    {
        flag.clear(boost::memory_order_release);
    }
};

LIVE Demo on Coliru

#include <boost/range/algorithm.hpp>
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <vector>

class SpinLock
{
    boost::atomic_flag flag;
public:
    void lock()
    {
        while( flag.test_and_set(boost::memory_order_acquire) )
            ;
    }
    bool try_lock()
    {
        return !flag.test_and_set(boost::memory_order_acquire);
    }
    void unlock()
    {
        flag.clear(boost::memory_order_release);
    }
};

int main()
{
    using namespace std; using namespace boost;

    SpinLock lock;
    vector<thread> v;
    for(auto i = 0; i!=4; ++i)
        v.emplace_back([&lock, i]
        {
            for(auto j = 0; j!=16; ++j)
            {
                this_thread::yield();
                lock_guard<SpinLock> x(lock);
                cout << "Hello from " << i << flush << "\tj = " << j << endl;
            }
        });
    for(auto &t: v)
        t.join();
}

Output is:

Hello from 0    j = 0
Hello from 1    j = 0
Hello from 3    j = 0
Hello from 2    j = 0
Hello from 3    j = 1
Hello from 1    j = 1
Hello from 3    j = 2
Hello from 2    j = 1
Hello from 1    j = 2
Hello from 2    j = 2
Hello from 1    j = 3
Hello from 2    j = 3
Hello from 1    j = 4
Hello from 3    j = 3
Hello from 2    j = 4
Hello from 1    j = 5
Hello from 2    j = 5
Hello from 1    j = 6
Hello from 2    j = 6
Hello from 1    j = 7
Hello from 2    j = 7
Hello from 1    j = 8
Hello from 2    j = 8
Hello from 3    j = 4
Hello from 2    j = 9
Hello from 3    j = 5
Hello from 1    j = 9
Hello from 2    j = 10
Hello from 1    j = 10
Hello from 2    j = 11
Hello from 3    j = 6
Hello from 1    j = 11
Hello from 2    j = 12
Hello from 3    j = 7
Hello from 1    j = 12
Hello from 2    j = 13
Hello from 3    j = 8
Hello from 2    j = 14
Hello from 3    j = 9
Hello from 1    j = 13
Hello from 2    j = 15
Hello from 1    j = 14
Hello from 3    j = 10
Hello from 1    j = 15
Hello from 3    j = 11
Hello from 3    j = 12
Hello from 3    j = 13
Hello from 3    j = 14
Hello from 3    j = 15
Hello from 0    j = 1
Hello from 0    j = 2
Hello from 0    j = 3
Hello from 0    j = 4
Hello from 0    j = 5
Hello from 0    j = 6
Hello from 0    j = 7
Hello from 0    j = 8
Hello from 0    j = 9
Hello from 0    j = 10
Hello from 0    j = 11
Hello from 0    j = 12
Hello from 0    j = 13
Hello from 0    j = 14
Hello from 0    j = 15
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54
0

Here is a sample using C++11 atomic:

#include <atomic>

typedef std::atomic<bool> Lock;

void enterCritical(Lock& lock) {
    bool unlocked = false;
    while (!lock.compare_exchange_weak(unlocked, true));
}

void exitCritical(Lock& lock) {
    lock.store(false);
}
erenon
  • 18,838
  • 2
  • 61
  • 93
  • 1. It is better to use `std::atomic_flag` for that purpose, because it is guaranteed to be lock-free. 2. There is `Boost.Atomic` library which also has `boost::atomic_flag`. – Evgeny Panasyuk Nov 02 '13 at 15:15
  • However, the atomic_flag interface is different: http://en.cppreference.com/w/cpp/atomic/atomic_flag -> you can't use compare and exchange, only test and set. – erenon Nov 02 '13 at 15:17
  • re2: Why use Boost if there's an STL equivalent? (Assuming C++11) – erenon Nov 02 '13 at 15:18
  • I am looking for a c++98 implementation – Rajeshwar Nov 02 '13 at 15:19
  • @erenon Yes, `atomic_flag` has different interface, but it is enough to implement spin-lock. Regarding `standard library` (not `STL`!) - yes, of course it is better to use it if available, but question was about `Boost`, so I presume it is not. – Evgeny Panasyuk Nov 02 '13 at 15:22
  • This is not safe implementation. cout seems crumbled together sometimes, but the boost::mutex won't. – Bryan Fok Mar 09 '15 at 08:04
0
The spinlock solution provided by erenon sometimes generated crumble cout result. but the boost::mutext solution won't. So either the solution is incorrect, or my understanding of cout is wrong.
#include <iostream>
#include <thread>
#include <atomic>
#include <boost/thread/mutex.hpp>
using namespace std;

class spinlock {
    private:
        std::atomic<bool> lock_;

    public:
        spinlock() {
            lock_.store(false);
        }

        void lock() {
            bool unlocked = false;
            while (!lock_.compare_exchange_weak(unlocked, true));

        }

        void unlock() {
            lock_.store(false);
        }
    };


class add_one
{
    private:
        std::string name_;
        unsigned int& num_;
        spinlock & lock_;
        boost::mutex & mutex_;
    public:
    add_one(std::string name, unsigned int& num, spinlock &lock, boost::mutex &mutex)
    :name_(name),
    num_(num),
    lock_(lock),
    mutex_(mutex)
    {

    }

    void add_and_display()
    {
        while(true)
        {
            lock_.lock();
            //boost::lock_guard<boost::mutex> g( mutex_);
            std::cout << name_ << " " << num_ << endl;
            if(num_ == 10000000)
            {

                return;
            }
            num_++;
            lock_.unlock();

        }
    }
};

int main()
{
   cout << "Hello World" << endl;
   unsigned int n = 0;
   spinlock lock;
   boost::mutex mutex;
   add_one one("t1", n ,lock, mutex);
   add_one two("t2", n ,lock, mutex);
   add_one three("t3", n ,lock, mutex);
   //add_one four("t4", n ,lock, mutex);
   std::thread t1(std::bind(&add_one::add_and_display, one));
   std::thread t2(std::bind(&add_one::add_and_display, two));
   std::thread t3(std::bind(&add_one::add_and_display, three));
   //std::thread t4(std::bind(&add_one::add_and_display, four));

   t1.join();
   t2.join();
   t3.join();
   //t4.join();

   return 0;
}
Bryan Fok
  • 3,277
  • 2
  • 31
  • 59
0

Slightly off-topic but relevant -- one can get a lock-free-guaranteed spinlock using C++11 (instead of C++03) without the need for boost (although that was the question title).

Using C++11 std::atomic_flag.

It is always lock-free, as noted by Evgeny Panasyuk in the comments of erenon's answer. std::atomic seems to not guarantee to be wait-free (which is required in some cases).

Also conforms to the BasicLockable interface, which can be used by std::lock_guard/std::scoped_lock/...

class Spinlock {
  std::atomic_flag _lock = ATOMIC_FLAG_INIT;

 public:
  void lock() {
    while (_lock.test_and_set(std::memory_order_acquire)) continue;
  }
  void unlock() {
    _lock.clear(std::memory_order_release);
  }
};
João Neto
  • 1,732
  • 17
  • 28