boost :: future - гарантированно ли вызывается wait_callback только один раз? - PullRequest
2 голосов
/ 20 февраля 2012

Если я установлю set_wait_callback на boost::unique_future, гарантированно ли он будет выполняться только один раз?

Я немного подозрительно, так как, глядя на исходный код, я нахожу следующее:

struct relocker
{
    boost::unique_lock<boost::mutex>& lock;

    relocker(boost::unique_lock<boost::mutex>& lock_):
        lock(lock_)
    {
        lock.unlock();
    }
    ~relocker()
    {
        lock.lock();
    }
private:
    relocker& operator=(relocker const&);
};

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=callback;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

void wait(bool rethrow=true)
{
    boost::unique_lock<boost::mutex> lock(mutex);
    do_callback(lock);
    while(!done)
    {
        waiters.wait(lock);
    }
    if(rethrow && exception)
    {
        boost::rethrow_exception(exception);
    }
}

Где в do_callback мьютекс фактически разблокирован во время вызова обратного вызова, что, насколько я понимаю, может привести к тому, что обратный вызов будет вызываться несколько раз, если несколько потоков вызывают функцию wait?

Может ли обратный вызов вызываться несколько раз? Это по дизайну? Или я что-то упустил?

Причина, по которой я немного удивлен, заключается в том, что в стандарте C ++ 11 async(std::launch::deferred, ...) (для которого set_wait_callback является двоюродным братом), похоже, имеет единственную гарантию вызова:

§30.6.8

Общее состояние не готово до завершения функции. Первый вызов к функции несвоевременного ожидания (30.6.4) объект асинхронного возврата, ссылающийся на это общее состояние , должен вызывать отложенная функция в потоке, вызвавшая функцию ожидания .

1 Ответ

2 голосов
/ 15 декабря 2012

Я думаю, что ваше подозрение обосновано. Код должен выглядеть как

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=callback;
        callback=boost::function<void()>;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

или даже

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && !done)
    {
        boost::function<void()> local_callback=boos::move(callback);
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

Once Boost.Function будет поддерживать семантику перемещения.

У этого есть еще некоторые проблемы, так как другой поток может вызвать set_wait_callback, поэтому обратный вызов может быть переназначен и могут быть вызваны два обратных вызова. Кажется, что требуется дополнительное состояние, чтобы указать, что обратный вызов уже выполнен.

void do_callback(boost::unique_lock<boost::mutex>& lock)
{
    if(callback && ! callback_done && !done)
    {
        boost::function<void()> local_callback=callback;
        callback_done=true;
        relocker relock(lock); // unlock mutex?
        local_callback();
    }
}    

Кстати, set_wait_callback не является поточно-ориентированным.

    template<typename F,typename U>
    void set_wait_callback(F f,U* u)
    {
        callback=boost::bind(f,boost::ref(*u));
    }

и должен быть защищен

    template<typename F,typename U>
    void set_wait_callback(F f,U* u)
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        callback=boost::bind(f,boost::ref(*u));
    }

Пожалуйста, не могли бы вы создать билет Trac для Boost Thread, чтобы эта проблема не была потеряна?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...