530 lines
14 KiB
C
530 lines
14 KiB
C
|
|
#pragma once
|
|||
|
|
#include <assert.h>
|
|||
|
|
#include <time.h>
|
|||
|
|
#include <cstring>
|
|||
|
|
#include <functional>
|
|||
|
|
#include <map>
|
|||
|
|
#include <memory>
|
|||
|
|
#include <set>
|
|||
|
|
#include <string>
|
|||
|
|
#include <vector>
|
|||
|
|
|
|||
|
|
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<std::string>& 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<int>& number_result, const std::string& is,
|
|||
|
|
char c) {
|
|||
|
|
std::vector<std::string> 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<std::string> ParseParam(const std::string& is, char c) {
|
|||
|
|
std::vector<std::string> 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<std::string>& 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<int>& 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<int> v;
|
|||
|
|
Text::SplitInt(v, input, CRON_SEPERATOR_ENUM);
|
|||
|
|
std::pair<int, int> 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<int> v;
|
|||
|
|
Text::SplitInt(v, input, CRON_SEPERATOR_RANGE);
|
|||
|
|
if (v.size() != 2) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int from = v[0];
|
|||
|
|
int to = v[1];
|
|||
|
|
std::pair<int, int> 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<int> v;
|
|||
|
|
Text::SplitInt(v, input, CRON_SEPERATOR_INTERVAL);
|
|||
|
|
if (v.size() != 2) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int from = v[0];
|
|||
|
|
int interval = v[1];
|
|||
|
|
std::pair<int, int> 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<int, int> 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<int, int> 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<void()>;
|
|||
|
|
using TimerPtr = std::shared_ptr<BaseTimer>;
|
|||
|
|
|
|||
|
|
class BaseTimer : public std::enable_shared_from_this<BaseTimer> {
|
|||
|
|
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<int> values; ///< 每一层时间轮上 有记录的值
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
class CronTimer : public BaseTimer {
|
|||
|
|
friend class TimerMgr;
|
|||
|
|
|
|||
|
|
public:
|
|||
|
|
CronTimer(TimerMgr& owner, std::vector<CronWheel>&& 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<int> 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<size_t, bool> 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<size_t, bool> 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<CronWheel> 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<std::string> v;
|
|||
|
|
Text::SplitStr(v, timer_string, ' ');
|
|||
|
|
if (v.size() != CronExpression::DT_MAX) {
|
|||
|
|
assert(false);
|
|||
|
|
return nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::vector<CronWheel> 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<CronTimer>(*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<LaterTimer>(*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<TimerPtr>
|
|||
|
|
* 否则新建 std::set<TimerPtr>
|
|||
|
|
* @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<TimerPtr> s;
|
|||
|
|
s.insert(p);
|
|||
|
|
m_timers[t] = s;
|
|||
|
|
} else {
|
|||
|
|
std::set<TimerPtr>& 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<TimerPtr>& s = it->second;
|
|||
|
|
s.erase(p);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
/**
|
|||
|
|
* @brief 需要调用函数的时间点-TimerPtr
|
|||
|
|
* key-value
|
|||
|
|
*/
|
|||
|
|
std::map<std::chrono::system_clock::time_point, std::set<TimerPtr>> 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
|