What's a proper way to use set_alert_notify to wake up main thread?

127 Views Asked by At

I'm trying to write my own torrent program based on libtorrent rasterbar and I'm having problems getting the alert mechanism working correctly. Libtorrent offers function

void set_alert_notify (boost::function<void()> const& fun);

which is supposed to

The intention of of the function is that the client wakes up its main thread, to poll for more alerts using pop_alerts(). If the notify function fails to do so, it won't be called again, until pop_alerts is called for some other reason.

so far so good, I think I understand the intention behind this function. However, my actual implementation doesn't work so good. My code so far is like this:

        std::unique_lock<std::mutex> ul(_alert_m);
        session.set_alert_notify([&]() { _alert_cv.notify_one(); });
        while (!_alert_loop_should_stop) {
            if (!session.wait_for_alert(std::chrono::seconds(0))) {
                _alert_cv.wait(ul);
            }
            std::vector<libtorrent::alert*> alerts;
            session.pop_alerts(&alerts);
            for (auto alert : alerts) {
                LTi_ << alert->message();
            }
        }

however there is a race condition. If wait_for_alert returns NULL (since no alerts yet) but the function passed to set_alert_notify is called before _alert_cw.wait(ul);, the whole loop waits forever (because of second sentence from the quote).

For the moment my solution is just changing _alert_cv.wait(ul); to _alert_cv.wait_for(ul, std::chrono::milliseconds(250)); which reduces number of loops per second enough while keeping latency low enough.

But it's really more workaround then solution and I keep thinking there must be proper way to handle this.

1

There are 1 best solutions below

3
On

You need a variable to record the notification. It should be protected by the same mutex that owns the condition variable.

bool _alert_pending;

session.set_alert_notify([&]() {
    std::lock_guard<std::mutex> lg(_alert_m);
    _alert_pending = true;
    _alert_cv.notify_one();
});
std::unique_lock<std::mutex> ul(_alert_m);
while(!_alert_loop_should_stop) {
    _alert_cv.wait(ul, [&]() {
         return _alert_pending || _alert_loop_should_stop;
    })
    if(_alert_pending) {
        _alert_pending = false;
        ul.unlock();
        session.pop_alerts(...);
        ...
        ul.lock();
    }
}