eis/TestProject/crontablCpp/cron_timer.h

530 lines
14 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.

#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