#ifndef HV_EVENT_LOOP_HPP_ #define HV_EVENT_LOOP_HPP_ #include #include #include #include #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 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; } else { loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE); } 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; 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) { 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 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 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) hv_gettid(); return hloop_tid(loop_); } bool isInLoopThread() { return hv_gettid() == hloop_tid(loop_); } void assertInLoopThread() { assert(isInLoopThread()); } void runInLoop(Functor fn) { if (isInLoopThread()) { if (fn) fn(); } else { queueInLoop(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_; std::mutex mutex_; std::queue customEvents; // GUAREDE_BY(mutex_) std::map timers; // GUAREDE_BY(mutex_) }; typedef std::shared_ptr EventLoopPtr; // ThreadLocalStorage static inline EventLoop* tlsEventLoop() { return (EventLoop*)ThreadLocalStorage::get(ThreadLocalStorage::EVENT_LOOP); } static inline TimerID setTimer(int timeout_ms, TimerCallback cb, int repeat = INFINITE) { EventLoop* loop = tlsEventLoop(); if (loop == NULL) return INVALID_TIMER_ID; return loop->setTimer(timeout_ms, cb, repeat); } static inline void killTimer(TimerID timerID) { EventLoop* loop = tlsEventLoop(); if (loop == NULL) return; loop->killTimer(timerID); } static inline void resetTimer(TimerID timerID) { EventLoop* loop = tlsEventLoop(); 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_