#pragma once #include #include #include #include #include #include #include #include #include namespace cron_timer { class Text { public: // Used to split strings separated by spaces, consecutive spaces are counted // as a separator static size_t SplitStr(std::vector& os, const std::string& is, char c) { os.clear(); auto start = is.find_first_not_of(c, 0); while (start != std::string::npos) { auto end = is.find_first_of(c, start); if (end == std::string::npos) { os.emplace_back(is.substr(start)); break; } else { os.emplace_back(is.substr(start, end - start)); start = is.find_first_not_of(c, end + 1); } } return os.size(); } static size_t SplitInt(std::vector& number_result, const std::string& is, char c) { std::vector string_result; SplitStr(string_result, is, c); number_result.clear(); for (size_t i = 0; i < string_result.size(); i++) { const std::string& value = string_result[i]; number_result.emplace_back(atoi(value.data())); } return number_result.size(); } static std::vector ParseParam(const std::string& is, char c) { std::vector result; ParseParam(result, is, c); return result; } // Used to segment strings separated by commas, consecutive commas are counted // as multiple delimiters static size_t ParseParam(std::vector& result, const std::string& is, char c) { result.clear(); size_t start = 0; while (start < is.size()) { auto end = is.find_first_of(c, start); if (end != std::string::npos) { result.emplace_back(is.substr(start, end - start)); start = end + 1; } else { result.emplace_back(is.substr(start)); break; } } if (start == is.size()) { result.emplace_back(std::string()); } return result.size(); } }; class CronExpression { public: enum DATA_TYPE { DT_SECOND = 0, DT_MINUTE = 1, DT_HOUR = 2, DT_DAY_OF_MONTH = 3, DT_MONTH = 4, DT_YEAR = 5, DT_MAX, }; /** * @brief 将 [sec,min,hour,day,mon,year] 的数值 取出 * @param input in 解除的子表达式 * @param data_type in 子表达式的类型 [sec,min,hour,day,mon,year] * @param values out 子表达式结果向量 * @return true * @return false */ static bool GetValues(const std::string& input, DATA_TYPE data_type, std::vector& values) { // // attention: enum seperater is ';' not ',' for using it in csv // static const char CRON_SEPERATOR_ENUM = ';'; ///<多个指定时间点 static const char CRON_SEPERATOR_RANGE = '-'; ///< a to b 时间范围 static const char CRON_SEPERATOR_INTERVAL = '/'; ///< a/b 表示 a, a+b, a+2b,…… if (input == "*") { auto pair_range = GetRangeFromType(data_type); for (auto i = pair_range.first; i <= pair_range.second; ++i) { values.push_back(i); } } else if (input.find_first_of(CRON_SEPERATOR_ENUM) != std::string::npos) { // enum std::vector v; Text::SplitInt(v, input, CRON_SEPERATOR_ENUM); std::pair pair_range = GetRangeFromType(data_type); for (auto value : v) { if (value < pair_range.first || value > pair_range.second) { return false; } values.push_back(value); } } else if (input.find_first_of(CRON_SEPERATOR_RANGE) != std::string::npos) { // range std::vector v; Text::SplitInt(v, input, CRON_SEPERATOR_RANGE); if (v.size() != 2) { return false; } int from = v[0]; int to = v[1]; std::pair pair_range = GetRangeFromType(data_type); if (from < pair_range.first || to > pair_range.second) { return false; } for (int i = from; i <= to; i++) { values.push_back(i); } } else if (input.find_first_of(CRON_SEPERATOR_INTERVAL) != std::string::npos) { // interval std::vector v; Text::SplitInt(v, input, CRON_SEPERATOR_INTERVAL); if (v.size() != 2) { return false; } int from = v[0]; int interval = v[1]; std::pair pair_range = GetRangeFromType(data_type); if (from < pair_range.first || interval < 0) { return false; } for (int i = from; i <= pair_range.second; i += interval) { values.push_back(i); } } else { // specific value std::pair pair_range = GetRangeFromType(data_type); int value = atoi(input.data()); if (value < pair_range.first || value > pair_range.second) { return false; } values.push_back(value); } assert(values.size() > 0); return values.size() > 0; } private: static std::pair GetRangeFromType(DATA_TYPE data_type) { int from = 0; int to = 0; switch (data_type) { case CronExpression::DT_SECOND: case CronExpression::DT_MINUTE: from = 0; to = 59; break; case CronExpression::DT_HOUR: from = 0; to = 23; break; case CronExpression::DT_DAY_OF_MONTH: from = 1; to = 31; break; case CronExpression::DT_MONTH: from = 1; to = 12; break; case CronExpression::DT_YEAR: from = 1970; to = 2099; break; case CronExpression::DT_MAX: assert(false); break; } return std::make_pair(from, to); } }; class TimerMgr; class BaseTimer; using FUNC_CALLBACK = std::function; using TimerPtr = std::shared_ptr; class BaseTimer : public std::enable_shared_from_this { friend class TimerMgr; public: BaseTimer(TimerMgr& owner, FUNC_CALLBACK&& func, int count) : m_owner(owner), m_func(std::move(func)), m_triggerTime(std::chrono::system_clock::now()), m_countLeft(count), m_canceled(false) {} virtual ~BaseTimer() {} inline void Cancel(); // trigger time of the timer std::chrono::system_clock::time_point GetTriggerTime() const { return m_triggerTime; } private: virtual void CreateTriggerTime(bool next) = 0; inline void DoFunc(); protected: TimerMgr& m_owner; FUNC_CALLBACK m_func; std::chrono::system_clock::time_point m_triggerTime; int m_countLeft; bool m_canceled; }; struct CronWheel { CronWheel() : cur_index(0) {} size_t cur_index; ///< values的下标 std::vector values; ///< 每一层时间轮上 有记录的值 }; class CronTimer : public BaseTimer { friend class TimerMgr; public: CronTimer(TimerMgr& owner, std::vector&& wheels, FUNC_CALLBACK&& func, int count) : BaseTimer(owner, std::move(func), count), m_wheels(std::move(wheels)) { tm local_tm; time_t time_now = time(nullptr); #ifdef _WIN32 localtime_s(&local_tm, &time_now); #else localtime_r(&time_now, &local_tm); #endif // _WIN32 std::vector init_values; init_values.push_back(local_tm.tm_sec); init_values.push_back(local_tm.tm_min); init_values.push_back(local_tm.tm_hour); init_values.push_back(local_tm.tm_mday); init_values.push_back(local_tm.tm_mon + 1); init_values.push_back(local_tm.tm_year + 1900); std::pair pairValue = std::make_pair(0, false); /*最近一次需要执行的时间*/ for (int i = CronExpression::DT_YEAR; i >= 0; i--) { pairValue = GetMinValid(i, init_values[i], pairValue.second); m_wheels[i].cur_index = pairValue.first; } } private: /** * @brief Create a Trigger Time object * 生成需要定时执行 的时间点 * @param next My Param doc */ virtual void CreateTriggerTime(bool next) { if (next) { Next(CronExpression::DT_SECOND); } tm next_tm; memset(&next_tm, 0, sizeof(next_tm)); next_tm.tm_sec = GetCurValue(CronExpression::DT_SECOND); next_tm.tm_min = GetCurValue(CronExpression::DT_MINUTE); next_tm.tm_hour = GetCurValue(CronExpression::DT_HOUR); next_tm.tm_mday = GetCurValue(CronExpression::DT_DAY_OF_MONTH); next_tm.tm_mon = GetCurValue(CronExpression::DT_MONTH) - 1; next_tm.tm_year = GetCurValue(CronExpression::DT_YEAR) - 1900; m_triggerTime = std::chrono::system_clock::from_time_t(mktime(&next_tm)); } // move to the next trigger time void Next(int data_type) { if (data_type >= CronExpression::DT_MAX) { // overflowed, this timer is invalid, should be removed m_canceled = true; return; } auto& wheel = m_wheels[data_type]; if (wheel.cur_index == wheel.values.size() - 1) { wheel.cur_index = 0; Next(data_type + 1); } else { ++wheel.cur_index; } } // return index, is changed std::pair GetMinValid(int data_type, int value, bool changed) const { auto& wheel = m_wheels[data_type]; if (changed) { return std::make_pair(0, true); } for (size_t i = 0; i < wheel.values.size(); i++) { if (wheel.values[i] < value) { continue; } else if (wheel.values[i] == value) { return std::make_pair(i, false); } else { return std::make_pair(i, true); } } return std::make_pair(0, true); } int GetCurValue(int data_type) const { const auto& wheel = m_wheels[data_type]; return wheel.values[wheel.cur_index]; } private: std::vector m_wheels; }; class LaterTimer : public BaseTimer { friend class TimerMgr; public: LaterTimer(TimerMgr& owner, int milliseconds, FUNC_CALLBACK&& func, int count) : BaseTimer(owner, std::move(func), count), m_milliSeconds(milliseconds) {} private: virtual void CreateTriggerTime(bool next) { m_triggerTime += std::chrono::milliseconds(m_milliSeconds); } private: const int m_milliSeconds; }; /** * @brief 时间轮 管理 */ class TimerMgr { /** * @brief 友元类 直接操作 友元类的public成员 */ friend class BaseTimer; friend class CronTimer; friend class LaterTimer; public: TimerMgr() {} TimerMgr(const TimerMgr&) = delete; const TimerMgr& operator=(const TimerMgr&) = delete; void Stop() { m_timers.clear(); } enum { RUN_FOREVER = 0, }; TimerPtr AddTimer(const std::string& timer_string, FUNC_CALLBACK&& func, int count = RUN_FOREVER) { std::vector v; Text::SplitStr(v, timer_string, ' '); if (v.size() != CronExpression::DT_MAX) { assert(false); return nullptr; } std::vector wheels; ///< [sec,min,hour,day,mon,year] for (int i = 0; i < CronExpression::DT_MAX; i++) { const auto& expression = v[i]; CronExpression::DATA_TYPE data_type = CronExpression::DATA_TYPE(i); CronWheel wheel; if (!CronExpression::GetValues(expression, data_type, wheel.values)) { assert(false); return nullptr; } wheels.emplace_back(wheel); } auto p = std::make_shared(*this, std::move(wheels), std::move(func), count); p->CreateTriggerTime(false); insert(p); return p; } TimerPtr AddDelayTimer(int milliseconds, FUNC_CALLBACK&& func, int count = 1) { assert(milliseconds > 0); milliseconds = (std::max)(milliseconds, 1); auto p = std::make_shared(*this, milliseconds, std::move(func), count); p->CreateTriggerTime(true); insert(p); return p; } std::chrono::system_clock::time_point GetNearestTime() { auto it = m_timers.begin(); if (it == m_timers.end()) { return (std::chrono::system_clock::time_point::max)(); } else { return it->first; } } size_t Update() { auto time_now = std::chrono::system_clock::now(); size_t count = 0; for (auto it = m_timers.begin(); it != m_timers.end();) { auto expire_time = it->first; ///<需要执行的时间点 if (expire_time > time_now) { break; ///<还未到执行时间 } // attention: this is a copy, not a reference auto timer_set = it->second; it = m_timers.erase(it); ///<当前时间点 所有需要被执行的函数 for (auto p : timer_set) { p->DoFunc(); ++count; } } return count; } private: /** * @brief 需要执行的插入时间-执行函数set * 如果已经存在的时间点,即 m_timers的key已存在,则插入 * TimerPtr到std::set * 否则新建 std::set * @param p My Param doc */ void insert(const TimerPtr& p) { auto t = p->GetTriggerTime(); auto it = m_timers.find(t); if (it == m_timers.end()) { std::set s; s.insert(p); m_timers[t] = s; } else { std::set& s = it->second; s.insert(p); } } void remove(const TimerPtr& p) { auto t = p->GetTriggerTime(); auto it = m_timers.find(t); if (it == m_timers.end()) { return; } std::set& s = it->second; s.erase(p); } private: /** * @brief 需要调用函数的时间点-TimerPtr * key-value */ std::map> m_timers; }; void BaseTimer::Cancel() { auto self = shared_from_this(); m_owner.remove(self); m_canceled = true; } void BaseTimer::DoFunc() { m_func(); ///<执行函数 CreateTriggerTime(true); ///<重置下次时间 // the timer can be cancelled in m_func() if (!m_canceled) { if (m_countLeft == TimerMgr::RUN_FOREVER || m_countLeft > 1) { if (m_countLeft > 1) { m_countLeft--; } auto self = shared_from_this(); m_owner.insert(self); } } } } // namespace cron_timer