emsApplication/sdk/include/hv/EventLoop.h

278 lines
7.4 KiB
C
Raw Normal View History

2024-05-24 12:23:42 +08:00
#ifndef HV_EVENT_LOOP_HPP_
#define HV_EVENT_LOOP_HPP_
#include <functional>
#include <queue>
#include <map>
#include <mutex>
#include "hloop.h"
#include "hthread.h"
#include "Status.h"
#include "Event.h"
#include "ThreadLocalStorage.h"
namespace hv {
class EventLoop : public Status {
public:
typedef std::function<void()> Functor;
// New an EventLoop using an existing hloop_t object,
// so we can embed an EventLoop object into the old application based on hloop.
// NOTE: Be careful to deal with destroy of hloop_t.
EventLoop(hloop_t* loop = NULL) {
setStatus(kInitializing);
if (loop) {
loop_ = loop;
is_loop_owner = false;
} else {
loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE);
is_loop_owner = true;
}
connectionNum = 0;
2024-05-24 12:29:09 +08:00
nextTimerID = 0;
2024-05-24 12:23:42 +08:00
setStatus(kInitialized);
}
~EventLoop() {
stop();
}
hloop_t* loop() {
return loop_;
}
// @brief Run loop forever
void run() {
if (loop_ == NULL) return;
if (status() == kRunning) return;
ThreadLocalStorage::set(ThreadLocalStorage::EVENT_LOOP, this);
setStatus(kRunning);
hloop_run(loop_);
setStatus(kStopped);
}
// stop thread-safe
void stop() {
if (loop_ == NULL) return;
if (status() < kRunning) {
if (is_loop_owner) {
hloop_free(&loop_);
}
loop_ = NULL;
return;
}
setStatus(kStopping);
hloop_stop(loop_);
loop_ = NULL;
}
void pause() {
if (loop_ == NULL) return;
if (isRunning()) {
hloop_pause(loop_);
setStatus(kPause);
}
}
void resume() {
if (loop_ == NULL) return;
if (isPause()) {
hloop_resume(loop_);
setStatus(kRunning);
}
}
// Timer interfaces: setTimer, killTimer, resetTimer
TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (loop_ == NULL) return INVALID_TIMER_ID;
2024-05-24 12:29:09 +08:00
assertInLoopThread();
2024-05-24 12:23:42 +08:00
htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
2024-05-24 12:29:09 +08:00
assert(htimer != NULL);
2024-05-24 12:23:42 +08:00
if (timerID == INVALID_TIMER_ID) {
2024-05-24 12:29:09 +08:00
timerID = generateTimerID();
2024-05-24 12:23:42 +08:00
}
2024-05-24 12:29:09 +08:00
hevent_set_id(htimer, timerID);
2024-05-24 12:23:42 +08:00
hevent_set_userdata(htimer, this);
2024-05-24 12:29:09 +08:00
timers[timerID] = std::make_shared<Timer>(htimer, cb, repeat);
return timerID;
}
// setTimerInLoop thread-safe
TimerID setTimerInLoop(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
if (timerID == INVALID_TIMER_ID) {
timerID = generateTimerID();
}
runInLoop(std::bind(&EventLoop::setTimer, this, timeout_ms, cb, repeat, timerID));
2024-05-24 12:23:42 +08:00
return timerID;
}
// alias javascript setTimeout, setInterval
2024-05-24 12:29:09 +08:00
// setTimeout thread-safe
2024-05-24 12:23:42 +08:00
TimerID setTimeout(int timeout_ms, TimerCallback cb) {
2024-05-24 12:29:09 +08:00
return setTimerInLoop(timeout_ms, cb, 1);
2024-05-24 12:23:42 +08:00
}
2024-05-24 12:29:09 +08:00
// setInterval thread-safe
2024-05-24 12:23:42 +08:00
TimerID setInterval(int interval_ms, TimerCallback cb) {
2024-05-24 12:29:09 +08:00
return setTimerInLoop(interval_ms, cb, INFINITE);
2024-05-24 12:23:42 +08:00
}
2024-05-24 12:29:09 +08:00
// killTimer thread-safe
2024-05-24 12:23:42 +08:00
void killTimer(TimerID timerID) {
2024-05-24 12:29:09 +08:00
runInLoop([timerID, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_del(iter->second->timer);
timers.erase(iter);
}
});
2024-05-24 12:23:42 +08:00
}
2024-05-24 12:29:09 +08:00
// resetTimer thread-safe
void resetTimer(TimerID timerID, int timeout_ms = 0) {
runInLoop([timerID, timeout_ms, this](){
auto iter = timers.find(timerID);
if (iter != timers.end()) {
htimer_reset(iter->second->timer, timeout_ms);
if (iter->second->repeat == 0) {
iter->second->repeat = 1;
}
2024-05-24 12:23:42 +08:00
}
2024-05-24 12:29:09 +08:00
});
2024-05-24 12:23:42 +08:00
}
long tid() {
if (loop_ == NULL) return hv_gettid();
return hloop_tid(loop_);
}
bool isInLoopThread() {
if (loop_ == NULL) return false;
return hv_gettid() == hloop_tid(loop_);
}
void assertInLoopThread() {
assert(isInLoopThread());
}
void runInLoop(Functor fn) {
if (isRunning() && isInLoopThread()) {
if (fn) fn();
} else {
queueInLoop(std::move(fn));
}
}
void queueInLoop(Functor fn) {
postEvent([fn](Event* ev) {
if (fn) fn();
});
}
void postEvent(EventCallback cb) {
if (loop_ == NULL) return;
2024-05-24 12:29:09 +08:00
EventPtr ev = std::make_shared<Event>(cb);
2024-05-24 12:23:42 +08:00
hevent_set_userdata(&ev->event, this);
ev->event.cb = onCustomEvent;
mutex_.lock();
customEvents.push(ev);
mutex_.unlock();
hloop_post_event(loop_, &ev->event);
}
private:
2024-05-24 12:29:09 +08:00
TimerID generateTimerID() {
return (((TimerID)tid() & 0xFFFFFFFF) << 32) | ++nextTimerID;
}
2024-05-24 12:23:42 +08:00
static void onTimer(htimer_t* htimer) {
EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
TimerID timerID = hevent_id(htimer);
TimerPtr timer = NULL;
auto iter = loop->timers.find(timerID);
if (iter != loop->timers.end()) {
timer = iter->second;
if (timer->repeat != INFINITE) --timer->repeat;
}
if (timer) {
if (timer->cb) timer->cb(timerID);
if (timer->repeat == 0) {
// htimer_t alloc and free by hloop, but timers[timerID] managed by EventLoop.
loop->timers.erase(timerID);
}
}
}
static void onCustomEvent(hevent_t* hev) {
EventLoop* loop = (EventLoop*)hevent_userdata(hev);
loop->mutex_.lock();
EventPtr ev = loop->customEvents.front();
loop->customEvents.pop();
loop->mutex_.unlock();
if (ev && ev->cb) ev->cb(ev.get());
}
public:
std::atomic<uint32_t> connectionNum; // for LB_LeastConnections
private:
hloop_t* loop_;
bool is_loop_owner;
std::mutex mutex_;
std::queue<EventPtr> customEvents; // GUAREDE_BY(mutex_)
2024-05-24 12:29:09 +08:00
std::map<TimerID, TimerPtr> timers;
std::atomic<TimerID> nextTimerID;
2024-05-24 12:23:42 +08:00
};
typedef std::shared_ptr<EventLoop> EventLoopPtr;
// ThreadLocalStorage
static inline EventLoop* tlsEventLoop() {
return (EventLoop*)ThreadLocalStorage::get(ThreadLocalStorage::EVENT_LOOP);
}
#define currentThreadEventLoop tlsEventLoop()
static inline TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return INVALID_TIMER_ID;
return loop->setTimer(timeout_ms, cb, repeat);
}
static inline void killTimer(TimerID timerID) {
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
loop->killTimer(timerID);
}
2024-05-24 12:29:09 +08:00
static inline void resetTimer(TimerID timerID, int timeout_ms) {
2024-05-24 12:23:42 +08:00
EventLoop* loop = tlsEventLoop();
assert(loop != NULL);
if (loop == NULL) return;
2024-05-24 12:29:09 +08:00
loop->resetTimer(timerID, timeout_ms);
2024-05-24 12:23:42 +08:00
}
static inline TimerID setTimeout(int timeout_ms, TimerCallback cb) {
return setTimer(timeout_ms, cb, 1);
}
static inline TimerID setInterval(int interval_ms, TimerCallback cb) {
return setTimer(interval_ms, cb, INFINITE);
}
}
#endif // HV_EVENT_LOOP_HPP_