Search code examples
c++multithreadingcondition-variablespinlock

Condition variable custom wait function


I created custom SpinLock class

I want to use this class in condition variable, but have an error

error: no matching function for call to ‘std::condition_variable::wait(std::unique_lock<Spinlock>&)’
                 cv_.wait(lk);

I have error in line cv_.wait(lk);

How can I support my SpinLock for condition variable?

I want to declare my own function wait void wait(unique_lock<SpinLock>& __lock, _Predicate __p) signature.

#include <atomic>
#include <iostream>
#include <string>
#include <cstdlib> // atoi
#include <thread> // thread
#include <mutex> // mutex
#include <condition_variable>
#include <vector>

using namespace std;


class Spinlock {
private:
    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);
    }
};


class PrintOrder final {
public:
    PrintOrder(int n, int threadNum)
        : maxNum_(n)
        , curNum_(0)
    {
        startTime_ = chrono::steady_clock::now();
        
        threads_.reserve(threadNum);
        unique_lock<Spinlock> lk(spinLock_);
        for(int x = 0; x < threadNum; ++x)
        {
            threads_.emplace_back(&PrintOrder::background, this, x);
        }
    }

    ~PrintOrder() {
        for(auto&& th : threads_)
        {
            th.join();
        }

        auto endTime = chrono::steady_clock::now();
        auto diff = endTime - startTime_;
        cout << chrono::duration <double, milli> (diff).count() << " ms" << endl;
    }

    void background(int x) {
        while(true) {
            unique_lock<Spinlock> lk(spinLock_);
            // wait until it's this thread's turn or curNum_ > maxNum_
            while((curNum_ % threads_.size()) != x and curNum_ <= maxNum_)
            {
                cv_.wait(lk);
            }
            if(curNum_ > maxNum_)
            {
                break;
            }

            cout << curNum_ << endl;
            ++curNum_;
            cv_.notify_all();
        }
    }

private:
    int maxNum_;
    int curNum_;
    Spinlock spinLock_;
    condition_variable cv_;
    vector<thread> threads_;

    chrono::time_point<chrono::steady_clock> startTime_;
};

int main(int argc, char **argv) {
    if (argc == 3)
    {
        int maxNum = atoi(argv[1]);
        int threadsNum = atoi(argv[2]);
        PrintOrder printOrder(maxNum, threadsNum);
    } else {
        cout << "ERROR: expected console input: <maxNum> <threadsNum>" << endl;
    }
    return 0;
}

Solution

  • std::condition_variable only supports std::unique_lock<std::mutex>. Use std::condition_variable_any instead.

    The condition_variable_any class is a generalization of std::condition_variable. Whereas std::condition_variable works only on std::unique_lock<std::mutex>, condition_variable_any can operate on any lock that meets the BasicLockable requirements.