Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> обертка для boost::deadline_timer 
:(
    Опции темы
Michrutka
Дата 29.3.2011, 14:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.2.2008

Репутация: нет
Всего: нет



Здравствуйте, господа форумчане.
Пишу кросплатформенный класс, реализующий таймер на основе boost::asio::deadline_timer.
io_service кот, передается в конструкторе - глобальный синглтон, работающий в отдельном потоке
Проблемы начинаются при уничтожении объекта.
В деструкторе я вызываю cancel у таймера и жду его завершения (если необходимо)

Вопрос - как мне понять, что таймер закончил работу (по собственному прерыванию, или по его методу cancel(), или _timer->expires_from_now() ?

Один из вариантов - по тому, что возвращают методы cancel(), или _timer->expires_from_now().
Они возвращают кол-во асинхронных опреаций, которые они прервали.
если они возращает != 0, то таймер должен вывалится в его handle с ошибкой error::operation_aborted.
если кол-во прерванных операций == 0, то он никуда вываливаться не будет.
НО согласно документации
"The boost::asio::basic_deadline_timer::expires_from_now() function cancels any pending asynchronous waits, and returns the number of asynchronous waits that were cancelled. If it returns 0 then you were too late and the wait handler has already been executed, or will soon be executed. If it returns 1 then the wait handler was successfully cancelled. "

То есть он може твернуть 0, и при этом вывалится в хендл. и, конечно же, он это делает, когда объекта уже не существует, и приложение на этом месте падает.
То есть 100% определить будет ли вызван handler после вызова cancel(), или _timer->expires_from_now()  невозможно??

Собственно, код:

*.h :
Код

class CycledJobExecutor
{
public:
    /**
     * Конструктор
     * @param ioService ссылка на ио сервис
     * @param period время срабатывания таймера,
     * @param job функция, которая будет вызвана после срабатывания таймера
     * @param startAfterConstructed запуск таймера сразу после создания объекта
     * @param loop перезапуск таймера после отработки
     */
    CycledJobExecutor(  io_service& ioService,
                        time_duration period,
                        boost::function<void()> job,
                        bool startAfterConstructed = true,
                        bool loop = true);

    /**
     * Запуск таймера со временем, переданным в метод
     * Время срабатывания таймера сохраняется в объекте
     * @param period время срабатывания таймера
     */
    virtual size_t WindUp(const time_duration& period);

    /**
     * Запуск таймера со временем, сохраненным в объекте.
     */
    virtual size_t WindUp();

    /**
     * Сброс таймера
     */
    virtual size_t Cancel();

    /**
     * @return время срабатывания таймера, сохраненног ов объекте
     */
    const time_duration& GetPeriod() const;

    //! Отменяет следующую после саморазрушения итерацию задачи _job
    virtual ~CycledJobExecutor();

protected:
    struct CycledJobState
    {
        enum Val
        {
            Running     = 0,
            Cancelling  = 1,
            Stopped     = 2,
            Stopping    = 3
        };
    };

    /**
     * Обрабатывает прерывание таймера
     */
    virtual void TimerExpiredHandler(const error_code& error);    

    /**
     * Функция, которая будет вызвана при срабатывании таймера
     */
    boost::function<void()> _job;

    /**
     * текуще состояние таймера
     **/
    volatile size_t _state;

    /**
     * Флаг, показывающий нужен ли перезапуск таймера, после его срабатывания
     */
    bool _loop;

    /**
     * Время срабатывания таймера
     */
    time_duration _period;
private:    
    boost::shared_ptr<deadline_timer> _timer;
    boost::mutex _mutex;
    boost::condition_variable _cv;
    log4cxx::LoggerPtr _logger;
};


*.cpp :
Код

CycledJobExecutor::CycledJobExecutor(io_service& ioService,
                                     time_duration period,
                                     boost::function<void()> job,
                                     bool startAfterConstructed,
                                     bool loop)
    :   _job(job),
        _state(CycledJobState::Stopped),
        _loop(loop),
        _period(period),
        _timer(new deadline_timer(ioService)),
        _mutex(),
        _cv()
{
    _loop = true;
    if (startAfterConstructed)
    {
        WindUp(_period);
    }
}

CycledJobExecutor::CycledJobExecutor(io_service& ioService,
                                     time_duration period,
                                     IJob& job,
                                     bool startAfterConstructed,
                                     bool loop)
    :   _job(boost::bind(&IJob::Do, &job)),
        _state(CycledJobState::Stopped),
        _loop(loop),
        _period(period),
        _timer(new deadline_timer(ioService)),
        _mutex(),
        _cv()
{
    if (startAfterConstructed)
    {
        WindUp(_period);
    }
}

CycledJobExecutor::~CycledJobExecutor()
{
    Cancel();
    boost::unique_lock<boost::mutex> lock(_mutex);
    while (_state != CycledJobState::Stopped)
    {
        _cv.wait(lock);
    }    
}

size_t CycledJobExecutor::WindUp(const time_duration& period)
{
    boost::unique_lock<boost::mutex> lock(_mutex);
    size_t cancelQnty = _timer->expires_from_now(_period);
    _period = period;
    if (_state != CycledJobState::Stopping)
    {
        _state = CycledJobState::Running;
        _timer->async_wait(bind(&CycledJobExecutor::TimerExpiredHandler, this, placeholders::error));
    }
    return cancelQnty;
}

size_t CycledJobExecutor::WindUp()
{
    return WindUp(_period);
}

size_t CycledJobExecutor::Cancel()
{
    boost::unique_lock<boost::mutex> lock(_mutex);
    _state = CycledJobState::Cancelling;
    size_t cancelQnty = _timer->cancel();
    if (cancelQnty == 0)
    {
        _state = CycledJobState::Stopped;
    }
    return cancelQnty;
}

const time_duration& CycledJobExecutor::GetPeriod() const
{
    return _period;
}

void CycledJobExecutor::TimerExpiredHandler(const error_code& error)
{
    
    if (error)
    {
        if (error != error::operation_aborted)
        {
            LOG4CXX_ERROR(_logger, "error code " <<  error);
        }
        else
        {
            if (_state != CycledJobState::Running)
            {
                boost::unique_lock<boost::mutex> lock(_mutex);
                _state = CycledJobState::Stopped;
            }
            _cv.notify_all();
        }        
        
    }
    else
    {
        if (_state == CycledJobState::Running)
        {
            if (_loop)
            {
                WindUp();
            }
            _job();
            if (!_loop)
            {
                {
                    boost::unique_lock<boost::mutex> lock(_mutex);
                    _state = CycledJobState::Stopped;
                }
                _cv.notify_all();
            }
        }
        else
        {
            {
                boost::unique_lock<boost::mutex> lock(_mutex);
                _state = CycledJobState::Stopped;
            }
            _cv.notify_all();
        }
    }
}



Вот и вопрос - как грамотно дождаться полной отработки таймера,
может ли при этом помочь удаление объекта - таймера?

Заранее спасибо.


PM MAIL   Вверх
bsa
Дата 29.3.2011, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Michrutka, фактически удалять объект надо только после отработки хэндла.
PM   Вверх
Michrutka
Дата 29.3.2011, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.2.2008

Репутация: нет
Всего: нет



bsa, ок. спасибо.
по сути вопрос и сводится к тому - как понять, что таймер в хендл больше однозначно не полезет.
PM MAIL   Вверх
boostcoder
Дата 29.3.2011, 17:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

Репутация: 49
Всего: 110



cancel() и в хэндлере проверяешь состояние ошибки.

Добавлено через 3 минуты и 41 секунду
Цитата(Michrutka @  29.3.2011,  14:33 Найти цитируемый пост)
если кол-во прерванных операций == 0, то он никуда вываливаться не будет.

если количество прерванных операций равно нулю, то это означает что операций нет, что в свою очередь означает что ни одна операция не была прервана, что в свою очередь означает что никакой хэндлер в любом случае не будет вызван ;)
PM WWW   Вверх
Michrutka
Дата 30.3.2011, 11:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.2.2008

Репутация: нет
Всего: нет



boostcoder
но "The boost::asio::basic_deadline_timer::expires_from_now() function cancels any pending asynchronous waits, and returns the number of asynchronous waits that were cancelled. If it returns 0 then you were too late and the wait handler has already been executed, or will soon be executed. If it returns 1 then the wait handler was successfully cancelled. "
это означает что он вот-вот туда вывалиться? и отработает? разве нет?

PM MAIL   Вверх
bsa
Дата 30.3.2011, 13:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Michrutkaboostcoder процитировал описание cancel() класса io_service.
В твоем случае нужно делать так: if (!timer->expires_from_now()) wait_for_handler_finished();

Это сообщение отредактировал(а) bsa - 30.3.2011, 13:39
PM   Вверх
Michrutka
Дата 30.3.2011, 14:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 57
Регистрация: 6.2.2008

Репутация: нет
Всего: нет



ясно. спасибо. 
буду копаться.
пока спасает от падений только некий тайм аут в деструкторе, что не очень красиво и хорошо.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0796 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.