Files
pip/libs/main/thread/pitimer.h

251 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file pitimer.h
* \ingroup Thread
* \~\brief
* \~english Timer object backed by an internal thread
* \~russian Объект таймера, работающий на внутреннем потоке
*
* \~\details
* \~english
* %PITimer uses an internal %PIThread to generate best-effort periodic ticks.
* When queued delivery is enabled, the timer also drains events addressed to it
* as performer through \a maybeCallQueuedEvents() on the main tick.
* \~russian
* %PITimer использует внутренний %PIThread для генерации периодических тиков
* с практической, но не строго гарантированной точностью. Когда включена
* отложенная доставка, таймер также обрабатывает события, адресованные ему как
* исполнителю, через \a maybeCallQueuedEvents() на основном тике.
*/
/*
PIP - Platform Independent Primitives
Timer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PITIMER_H
#define PITIMER_H
#include "piconditionvar.h"
#include "piobject.h"
#include "pisystemtime.h"
class PIThread;
//! \~\ingroup Thread
//! \~\brief
//! \~english Periodic timer that emits ticks from an internal worker thread.
//! \~russian Периодический таймер, который выдает тики из внутреннего рабочего потока.
//! \~\details
//! \~english
//! The main tick uses delimiter value 1. Additional frequency delimiters can
//! request extra callbacks every \a n ticks. This class does not promise exact
//! wake-up timing or scheduler fairness.
//! \~russian
//! Основной тик использует значение делителя 1. Дополнительные делители частоты
//! позволяют запрашивать дополнительные вызовы каждые \a n тиков. Этот класс не
//! обещает точное время пробуждения или справедливость планировщика.
class PIP_EXPORT PITimer: public PIObject {
PIOBJECT_SUBCLASS(PITimer, PIObject);
public:
NO_COPY_CLASS(PITimer);
//! \~english Constructs a timer without a tick callback.
//! \~russian Создает таймер без обратного вызова тика.
explicit PITimer();
//! \~english Constructs a timer with a callback that receives the delimiter value.
//! \~russian Создает таймер с обратным вызовом, принимающим значение делителя.
explicit PITimer(std::function<void(int)> func);
//! \~english Constructs a timer with a callback that ignores the delimiter value.
//! \~russian Создает таймер с обратным вызовом, игнорирующим значение делителя.
explicit PITimer(std::function<void()> func);
//! \~english Destroys the timer and stops its internal worker thread.
//! \~russian Уничтожает таймер и останавливает его внутренний рабочий поток.
virtual ~PITimer();
//! \~english Returns the configured base interval between main ticks.
//! \~russian Возвращает настроенный базовый интервал между основными тиками.
PISystemTime interval() const;
//! \~english Sets the base interval. If the timer is running, restarts it with the new value.
//! \~russian Устанавливает базовый интервал. Если таймер запущен, перезапускает его с новым значением.
void setInterval(PISystemTime interval);
//! \~english Returns whether the timer thread is currently running.
//! \~russian Возвращает, работает ли сейчас поток таймера.
bool isRunning() const;
//! \~english Returns whether stop has been requested and shutdown is still in progress.
//! \~russian Возвращает, был ли запрошен останов и идет ли еще завершение.
bool isStopping() const;
//! \~english Waits until the timer finishes. Returns \b false if the timeout expires first.
//! \~russian Ожидает завершения таймера. Возвращает \b false, если таймаут истек раньше.
bool waitForFinish(PISystemTime timeout = {});
//! \~english Sets the base interval and starts the timer.
//! \~russian Устанавливает базовый интервал и запускает таймер.
bool start(PISystemTime interval);
//! \~english Sets the base interval and tick callback, then starts the timer.
//! \~russian Устанавливает базовый интервал и обратный вызов тика, затем запускает таймер.
bool start(PISystemTime interval, std::function<void()> func);
//! \~english Deprecated overload of \a start() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a start(), принимающая миллисекунды.
bool start(double interval_ms) DEPRECATEDM("use start(PISystemTime)") { return start(PISystemTime::fromMilliseconds(interval_ms)); }
//! \~english Deprecated overload of \a stopAndWait() that accepts milliseconds.
//! \~russian Устаревшая перегрузка \a stopAndWait(), принимающая миллисекунды.
void stopAndWait(int timeout_ms) { stopAndWait(PISystemTime::fromMilliseconds(timeout_ms)); }
//! \~english Requests stop and waits for the worker thread to finish.
//! \~russian Запрашивает остановку и ожидает завершения рабочего потока.
void stopAndWait(PISystemTime timeout = {});
//! \~english Sets a tick callback that ignores the delimiter value.
//! \~russian Устанавливает обратный вызов тика, игнорирующий значение делителя.
void setSlot(std::function<void()> func);
//! \~english Sets a tick callback that receives the current delimiter value.
//! \~russian Устанавливает обратный вызов тика, принимающий текущее значение делителя.
void setSlot(std::function<void(int)> func);
//! \~english Enables locking of the internal mutex around tick processing.
//! \~russian Включает блокировку внутреннего мьютекса вокруг обработки тиков.
void needLockRun(bool need) { lockRun = need; }
//! \~english Returns whether the timer drains queued delivery for itself as performer on each main tick. By default \b true.
//! \~russian Возвращает, должен ли таймер обрабатывать отложенную доставку для себя как исполнителя на каждом основном тике. По
//! умолчанию \b true.
bool isCallQueuedEvents() const { return callEvents; }
//! \~english Enables or disables queued-delivery draining through \a maybeCallQueuedEvents() on each main tick.
//! \~russian Включает или отключает обработку отложенной доставки через \a maybeCallQueuedEvents() на каждом основном тике.
void setCallQueuedEvents(bool yes) { callEvents = yes; }
//! \~english Adds a frequency delimiter that invokes \a func every \a delim main ticks.
//! \~russian Добавляет делитель частоты, который вызывает \a func каждые \a delim основных тиков.
void addDelimiter(int delim, std::function<void(int)> func = nullptr);
//! \~english Adds a delimiter with a callback that ignores the delimiter value.
//! \~russian Добавляет делитель с обратным вызовом, игнорирующим значение делителя.
void addDelimiter(int delim, std::function<void()> func);
//! \~english Adds a delimiter with a pointer-based callback signature.
//! \~russian Добавляет делитель с сигнатурой обратного вызова, принимающей указатель.
void addDelimiter(int delim, std::function<void(void *)> slot);
//! \~english Removes all delimiters with value \a delim.
//! \~russian Удаляет все делители со значением \a delim.
void removeDelimiter(int delim);
//! \handlers
//! \{
//! \fn bool start()
//! \brief
//! \~english Starts the timer with the current \a interval().
//! \~russian Запускает таймер с текущим значением \a interval().
EVENT_HANDLER0(bool, start);
//! \fn bool restart()
//! \brief
//! \~english Stops the timer, then starts it again with the current \a interval().
//! \~russian Останавливает таймер, затем снова запускает его с текущим значением \a interval().
EVENT_HANDLER0(bool, restart);
//! \fn bool stop()
//! \brief
//! \~english Requests stop and wakes the timer thread, but does not wait for completion.
//! \~russian Запрашивает остановку и пробуждает поток таймера, но не ожидает завершения.
EVENT_HANDLER0(void, stop);
//! \fn void clearDelimiters()
//! \brief
//! \~english Remove all frequency delimiters
//! \~russian Удаляет все делители частоты
EVENT_HANDLER0(void, clearDelimiters) { delims.clear(); }
//! \fn void lock()
//! \brief
//! \~english Locks the internal timer mutex.
//! \~russian Блокирует внутренний мьютекс таймера.
EVENT_HANDLER0(void, lock) { mutex_.lock(); }
//! \fn void unlock()
//! \brief
//! \~english Unlocks the internal timer mutex.
//! \~russian Разблокирует внутренний мьютекс таймера.
EVENT_HANDLER0(void, unlock) { mutex_.unlock(); }
//! \}
//! \events
//! \{
//! \fn void tickEvent(int delimiter)
//! \brief
//! \~english Raised on each timer tick and on configured delimiter ticks.
//! \~russian Вызывается на каждом тике таймера и на настроенных тиках делителей.
//! \~\details
//! \~english
//! "delimiter" is the frequency delimiter, 1 for the main loop.
//! \~russian
//! "delimiter" - делитель частоты, 1 для основного цикла.
EVENT1(tickEvent, int, delimiter);
//! \}
protected:
struct PIP_EXPORT Delimiter {
Delimiter(std::function<void(int)> func_ = nullptr, int delim_ = 1) {
func = std::move(func_);
delim = delim_;
}
std::function<void(int)> func;
int delim = 0;
int tick = 0;
};
void initFirst();
void init() const;
void threadFunc();
void adjustTimes();
void execTick();
//! \~english Virtual tick method. The main loop passes delimiter 1, additional delimiters pass their own value.
//! \~russian Виртуальный метод тика. Основной цикл передает делитель 1, дополнительные делители передают свое значение.
virtual void tick(int delimiter) {}
PIThread * thread = nullptr;
std::atomic_bool lockRun = {false}, callEvents = {true};
PIMutex mutex_;
PISystemTime m_interval, m_interval_x5;
PISystemTime m_time_next;
std::function<void()> ret_func = nullptr;
std::function<void(int)> ret_func_delim = nullptr;
PIVector<Delimiter> delims;
PIConditionVariable event;
};
#endif // PITIMER_H