0

Suppose I have a class with the following interface:

class IEvent
{
    void SetEvent() = 0;
    void WaitForEvent() = 0;
}

WaitForEvent() is a blocking function which waits until another thread calls SetEvent() function.

I'm writing unit tests for this class and want following scenario:

First thread calls WaitForEvent(). After that second thread calls SetEvent().

How to synchronize this threads that SetEvent() call will always follow WaitForEvent() call?

I don't want to use any sleeps because I want unit tests to run as fast as possible.

mort
  • 12,988
  • 14
  • 52
  • 97
Nosturion
  • 289
  • 3
  • 12

1 Answers1

1

This was the best I could manage

#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>

class IEvent
{
public:
    virtual void SetEvent() = 0;
    virtual void WaitForEvent() = 0;
};

class EventClass : public IEvent
{
public:
    void SetEvent() override {
        std::unique_lock<std::mutex> lock { _mutex };
        std::cout << "setting event\n";
        _event = true;
        lock.unlock();
        _cv.notify_all();
    }

    void WaitForEvent() override {
        std::unique_lock<std::mutex> lock { _mutex };
        std::cout << "waiting for event\n";
        _cv.wait(lock, [this]{ return _event; });
        std::cout << "waiting complete\n";
    };

private:
    std::mutex _mutex;
    std::condition_variable _cv;
    bool _event = false;
};

int main()
{
    EventClass object;

    std::mutex cv_mutex;
    std::condition_variable may_set_cv;
    bool may_set_event = false;

    auto setting_thread = std::thread([&]{
        std::unique_lock<std::mutex> lock { cv_mutex };
        may_set_cv.wait(lock,
                        [&] {
                            return may_set_event;
                        });
        object.SetEvent();
    });
    std::unique_lock<std::mutex> lock { cv_mutex };
    may_set_event = true;
    lock.unlock();
    may_set_cv.notify_one();

    // tiny race condition here

    object.WaitForEvent();

    setting_thread.join();
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thank you. I've considered something similar (non C++11). If there is no perfect solution this would be my choice. – Nosturion May 08 '15 at 12:18
  • 1
    It's near-perfect. The only thing you can't control is the few clock cycles between the notify call and calling WaitForEvent - we're talking nanoseconds here. If the second thread is able to wake up in that time and preempt the call, I'll be a monkey's uncle :-) – Richard Hodges May 08 '15 at 12:22