| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- #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;
- }
- setStatus(kInitialized);
- }
- ~EventLoop() {
- stop();
- }
- hloop_t* loop() {
- return loop_;
- }
- // @brief Run loop forever
- void run() {
- if (loop_ == NULL) return;
- ThreadLocalStorage::set(ThreadLocalStorage::EVENT_LOOP, this);
- setStatus(kRunning);
- hloop_run(loop_);
- setStatus(kStopped);
- }
- 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, int repeat = INFINITE) {
- if (loop_ == NULL) return INVALID_TIMER_ID;
- htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
- Timer timer(htimer, cb, repeat);
- hevent_set_userdata(htimer, this);
- TimerID timerID = hevent_id(htimer);
- mutex_.lock();
- timers[timerID] = timer;
- mutex_.unlock();
- return timerID;
- }
- // alias javascript setTimeout, setInterval
- TimerID setTimeout(int timeout_ms, TimerCallback cb) {
- return setTimer(timeout_ms, cb, 1);
- }
- TimerID setInterval(int interval_ms, TimerCallback cb) {
- return setTimer(interval_ms, cb, INFINITE);
- }
- void killTimer(TimerID timerID) {
- std::lock_guard<std::mutex> locker(mutex_);
- auto iter = timers.find(timerID);
- if (iter != timers.end()) {
- Timer& timer = iter->second;
- htimer_del(timer.timer);
- timers.erase(iter);
- }
- }
- void resetTimer(TimerID timerID) {
- std::lock_guard<std::mutex> locker(mutex_);
- auto iter = timers.find(timerID);
- if (iter != timers.end()) {
- Timer& timer = iter->second;
- htimer_reset(timer.timer);
- if (timer.repeat == 0) {
- timer.repeat = 1;
- }
- }
- }
- 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;
- EventPtr ev(new Event(cb));
- hevent_set_userdata(&ev->event, this);
- ev->event.cb = onCustomEvent;
- mutex_.lock();
- customEvents.push(ev);
- mutex_.unlock();
- hloop_post_event(loop_, &ev->event);
- }
- private:
- static void onTimer(htimer_t* htimer) {
- EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
- TimerID timerID = hevent_id(htimer);
- TimerCallback cb = NULL;
- loop->mutex_.lock();
- auto iter = loop->timers.find(timerID);
- if (iter != loop->timers.end()) {
- Timer& timer = iter->second;
- cb = timer.cb;
- --timer.repeat;
- }
- loop->mutex_.unlock();
- if (cb) cb(timerID);
- // NOTE: refind iterator, because iterator may be invalid
- // if the timer-related interface is called in the callback function above.
- loop->mutex_.lock();
- iter = loop->timers.find(timerID);
- if (iter != loop->timers.end()) {
- Timer& timer = iter->second;
- if (timer.repeat == 0) {
- // htimer_t alloc and free by hloop, but timers[timerID] managed by EventLoop.
- loop->timers.erase(iter);
- }
- }
- loop->mutex_.unlock();
- }
- 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());
- }
- private:
- hloop_t* loop_;
- bool is_loop_owner;
- std::mutex mutex_;
- std::queue<EventPtr> customEvents; // GUAREDE_BY(mutex_)
- std::map<TimerID, Timer> timers; // GUAREDE_BY(mutex_)
- };
- 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, int 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);
- }
- static inline void resetTimer(TimerID timerID) {
- EventLoop* loop = tlsEventLoop();
- assert(loop != NULL);
- if (loop == NULL) return;
- loop->resetTimer(timerID);
- }
- 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_
|