-
Notifications
You must be signed in to change notification settings - Fork 0
/
Timer.hpp
279 lines (243 loc) · 9.71 KB
/
Timer.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/// \file fon9/Timer.hpp
/// \author fonwinz@gmail.com
#ifndef __fon9_Timer_hpp__
#define __fon9_Timer_hpp__
#include "fon9/SortedVector.hpp"
#include "fon9/intrusive_ref_counter.hpp"
#include "fon9/TimeStamp.hpp"
#include "fon9/ThreadController.hpp"
namespace fon9 {
enum class TimerSeqNo : uint64_t {
/// - 沒有在 TimerThread 裡面等候執行.
/// - 有可能的狀態:
/// - 正在 TimerThread 觸發 OnTimer 事件.
/// - 尚未啟動 Run...() 函式.
NoWaiting = 0,
/// Timer 已呼叫 Dispose...() 相關函式, 無法再啟動 Run...() 相關函式.
Disposed = 1,
/// 已放入 TimerThread 裡面等候執行.
WaitInLine = 2,
};
inline bool IsTimerWaitInLine(TimerSeqNo seqno) {
static_assert(std::is_unsigned<underlying_type_t<TimerSeqNo>>::value, "underlying_type_t<TimerSeqNo> must be unsigned");
return seqno >= TimerSeqNo::WaitInLine;
}
struct TimerEntryKey {
TimeStamp EmitTime_{TimeStamp::Null()};
/// 非0表示:已啟動計時器,等候觸發.
TimerSeqNo SeqNo_{TimerSeqNo::NoWaiting};
/// 把時間較小的排到 SortedVector 尾端 (先觸發, vector 移除尾端的負擔很小),
/// 所以傳回值是 > 的結果.
bool operator< (const TimerEntryKey& rhs) const {
return fon9_UNLIKELY(this->EmitTime_ == rhs.EmitTime_)
? this->SeqNo_ > rhs.SeqNo_
: this->EmitTime_ > rhs.EmitTime_;
}
};
/// \ingroup Thrs
/// - 每個 TimerThread 擁有一個自己的 thread.
/// - 每個 timer 啟動時, 不論設定的是 [間隔時間] or [絕對時間], 都會使用 TimeStamp_ 來處理.
/// - 所以如果使用「間隔時間」啟動 timer, 當系統時間有變動時, 則無法在正確的「時間間隔」觸發事件!
/// - thread 的睡眠時間: 最接近的 timer.TimeStamp_ - now;
class fon9_API TimerThread;
using TimerThreadSP = intrusive_ptr<TimerThread>;
/// \ingroup Thrs
/// 這裡提供一個 fon9 預設的 TimerThread.
/// 第一次呼叫時才會啟動. 啟動後, 程式結束時才會解構.
fon9_API TimerThreadSP GetDefaultTimerThread();
fon9_WARN_DISABLE_PADDING;
/// \ingroup Thrs
/// 使用一個「額外共用的 TimerThread」提供計時功能.
/// - 在 OnTimer() 被觸發時, 至少還有一個 TimerEntrySP 保留 timer.
/// - 如果要用 data member 的方式使用 TimerEntry:
/// \code
/// class TimerUser {
/// struct Timer : public fon9::TimerEntry {
/// Timer() : TimerEntry{fon9::GetDefaultTimerThread()} {
/// }
/// virtual void OnTimerEntryReleased() override {
/// // Timer 作為 data member, 不需要 delete, 所以 OnTimerEntryReleased(): do nothing.
/// }
/// virtual void EmitOnTimer(fon9::TimeStamp now) override {
/// TimerUser* user = &fon9::ContainerOf(*this, &TimerUser::Timer_);
/// ... do something ...
/// delete user;
/// }
/// };
/// Timer Timer_;
/// public:
/// ~TimerUser() {
/// // 解構時必須確定(並等候) Timer 已停止計時, 並終止其他 thread 的 StopAndWait().
/// this->Timer_.DisposeAndWait();
/// }
/// };
/// \endcode
class fon9_API TimerEntry : public intrusive_ref_counter<TimerEntry> {
fon9_NON_COPY_NON_MOVE(TimerEntry);
/// default: do nothing;
virtual void OnTimer(TimeStamp now);
/// default: OnTimer(); intrusive_ptr_release(this);
virtual void EmitOnTimer(TimeStamp now);
/// default: delete this;
virtual void OnTimerEntryReleased();
inline friend void intrusive_ptr_deleter(const TimerEntry* entry) {
const_cast<TimerEntry*>(entry)->OnTimerEntryReleased();
}
friend class TimerThread;
TimerEntryKey Key_;
void SetupRun(TimeStamp atTimePoint, const TimeInterval* after);
public:
const TimerThreadSP TimerThread_;
TimerEntry(TimerThreadSP timerThread) : TimerThread_(std::move(timerThread)) {
}
virtual ~TimerEntry();
const TimerEntryKey& GetKey() const {
return Key_;
}
/// 解構前的處置.
/// 一旦呼叫 Dispose 就無法再進行計時.
void DisposeNoWait();
/// 解構前的處置.
/// 若 TimerThread 正在觸發事件, 則等候事件處理完畢.
void DisposeAndWait();
/// 從計時隊列中移除, 但如果 this 已取出, 準備觸發 OnTimer(),
/// 則有可能在返回後仍觸發 OnTimer();
void StopNoWait();
/// 從計時隊列中移除, 但如果 this 已取出, 準備觸發 OnTimer(),
/// 則會等 OnTimer() 結束後才會返回 StopAndWait().
/// 並將 EmitTime 設為 TimeStamp::Null()
void StopAndWait();
void RunAt(TimeStamp atTimePoint) {
this->SetupRun(atTimePoint, nullptr);
}
void RunAfter(TimeInterval after) {
this->SetupRun(TimeStamp{UtcNow() + after}, &after);
}
};
/// \ingroup Thrs
/// 使用 data member 的方式處理 Timer.
class fon9_API DataMemberTimer : public TimerEntry {
fon9_NON_COPY_NON_MOVE(DataMemberTimer);
/// Timer 作為 data member, 不需要 delete, 所以 OnTimerEntryReleased(): do nothing.
virtual void OnTimerEntryReleased() override;
/// Timer 作為 data member, 必須採用 EmitOnTimer() 的方式觸發.
/// 衍生者必須完成此函式.
virtual void EmitOnTimer(TimeStamp now) override = 0;
public:
/// 使用 GetDefaultTimerThread() 當作 timer thread.
DataMemberTimer();
DataMemberTimer(TimerThreadSP timerThread);
};
/// \ingroup Thrs
/// 使用 [data member] + [static member function] 的方式處理 Timer.
/// \code
/// class MyObject {
/// static void EmitOnTimer1(fon9::TimerEntry* timer, fon9::TimeStamp now) {
/// MyObject& rthis = fon9::ContainerOf(*static_cast<decltype(MyObject::Timer1_)*>(timer), &MyObject::Timer1_);
/// // 處理 Timer1 事件...
/// }
/// fon9::DataMemberEmitOnTimer<&MyObject::EmitOnTimer1> Timer1_;
/// };
/// \endcode
template <void (*pEmitOnTimer)(TimerEntry*, TimeStamp now)>
class DataMemberEmitOnTimer : public DataMemberTimer {
fon9_NON_COPY_NON_MOVE(DataMemberEmitOnTimer);
using base = DataMemberTimer;
virtual void EmitOnTimer(TimeStamp now) override {
pEmitOnTimer(this, now);
}
public:
using base::base;
/// 使用 GetDefaultTimerThread() 當作 timer thread.
DataMemberEmitOnTimer() = default;
};
fon9_MSC_WARN_POP;
using TimerEntrySP = intrusive_ptr<TimerEntry>;
/// \ingroup Thrs
/// 如果「時間到」的時候「使用 weak_ptr 參考的 owner」依然存活,
/// 才會觸發 owner->OnTimer(this) 事件, TimerEntry_OwnerWP 寫法範例:
/// \code
/// class MySession : public std::enable_shared_from_this<MySession> {
/// fon9_NON_COPY_NON_MOVE(MySession);
///
/// void OnTimer(fon9::TimerEntry* timer, fon9::TimeStamp now) {
/// ... 計時時間到 ...
/// timer->RunAfter(fon9::TimeInterval_Second(1)); // 如果有需要計時, 則需要再啟動一次.
/// }
/// using FlowTimer = fon9::TimerEntry_OwnerWP<MySession, &MySession::OnTimer>;
/// fon9::TimerEntrySP FlowTimer_;
///
/// MySession() = default;
/// public:
/// ~MySession() {
/// // 解構時不須針對 FlowTimer_ 做額外處置:
/// // - 進入解構程序時, FlowTimer_->Owner_ (weak_ptr) lock 會失敗, 所以不可能進入 MySession::OnTimer()
/// // - 當最後一個 EntrySP 死亡時, FlowTimer_ 就會自然死亡了!
/// }
/// static std::shared_ptr<MySession> Make(fon9::TimerThreadSP timerThread) {
/// std::shared_ptr<MySession> res{new MySession{}};
/// res->FlowTimer_.reset(new FlowTimer{std::move(timerThread),res}); // 建立計時器.
/// res->FlowTimer_->RunAfter(fon9::TimeInterval_Second(1)); // 啟動計時器.
/// return res;
/// }
/// };
/// \endcode
template <class OwnerT, void (OwnerT::*pOnTimer)(TimerEntry*, TimeStamp now)>
class TimerEntry_OwnerWP : public TimerEntry {
fon9_NON_COPY_NON_MOVE(TimerEntry_OwnerWP);
std::weak_ptr<OwnerT> Owner_;
protected:
virtual void OnTimer(TimeStamp now) override {
if (std::shared_ptr<OwnerT> owner = this->Owner_.lock())
(owner.get()->*pOnTimer)(this, now);
}
public:
TimerEntry_OwnerWP(TimerThreadSP timerThread, std::weak_ptr<OwnerT> owner)
: TimerEntry{std::move(timerThread)}
, Owner_{std::move(owner)} {
}
};
fon9_WARN_DISABLE_PADDING;
/// \ingroup Thrs
/// 實際的 TimerEntry 放在 TimerThread 裡面執行.
class fon9_API TimerThread : public intrusive_ref_counter<TimerThread> {
fon9_NON_COPY_NON_MOVE(TimerThread);
friend class TimerEntry;
struct TimerThreadData {
void Erase(TimerEntryKey& key) {
if (key.SeqNo_ == TimerSeqNo::NoWaiting)
return;
auto ifind = this->Timers_.find(key);
if (ifind != this->Timers_.end())
this->Timers_.erase(ifind);
}
using Timers = SortedVector<TimerEntryKey, TimerEntrySP>;
TimerSeqNo LastSeqNo_{TimerSeqNo::WaitInLine};
Timers Timers_;
TimeInterval CvWaitSecs_;
/// 如果在 TimerThread 正在觸發, 則會設定此值.
/// 讓另一 thread 呼叫 TimerEntry::StopAndWait() 時, 可以等到 OnTimer() 真的結束後才返回.
TimerEntry* CurrEntry_{};
};
using TimerController = ThreadController<TimerThreadData, WaitPolicy_CV>;
using Locker = TimerController::Locker;
TimerController TimerController_;
std::thread Thread_;
bool CheckCurrEmit(Locker& timerThread, TimerEntry& timer);
bool RunTimer(Locker&);
protected:
void ThrRun(std::string timerName);
void NotifyForEndNow() {
this->TimerController_.NotifyForEndNow();
}
public:
TimerThread(std::string timerName);
virtual ~TimerThread();
void WaitForEndNow();
bool InThisThread() const {
return (this->Thread_.get_id() == std::this_thread::get_id());
}
};
fon9_MSC_WARN_POP;
} // namespace fon9
#endif//__fon9_Timer_hpp__