#ifndef HV_EVENT_LOOP_HPP_ #define HV_EVENT_LOOP_HPP_ #include #include #include #include #include "hloop.h" #include "hthread.h" #include "Event.h" namespace hv { class EventLoop { public: typedef std::function Functor; EventLoop() { loop_ = hloop_new(HLOOP_FLAG_AUTO_FREE); assert(loop_ != NULL); hloop_set_userdata(loop_, this); } ~EventLoop() { stop(); } void start() { if (loop_ == NULL) return; hloop_run(loop_); } void stop() { if (loop_ == NULL) return; hloop_stop(loop_); loop_ = NULL; } void pause() { if (loop_ == NULL) return; hloop_pause(loop_); } void resume() { if (loop_ == NULL) return; hloop_resume(loop_); } 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, &timer); 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; } } } bool isInLoop() { return hv_gettid() == hloop_tid(loop_); } void assertInLoop() { assert(isInLoop()); } void runInLoop(Functor fn) { if (isInLoop()) { 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)); ev->event.cb = onCustomEvent; mutex_.lock(); customEvents.push(ev); mutex_.unlock(); hloop_post_event(loop_, &ev->event); } private: static void onTimer(htimer_t* htimer) { hloop_t* hloop = (hloop_t*)hevent_loop(htimer); EventLoop* loop = (EventLoop*)hloop_userdata(hloop); 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) { hloop_t* hloop = (hloop_t*)hevent_loop(hev); EventLoop* loop = (EventLoop*)hloop_userdata(hloop); 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_) }; } #endif // HV_EVENT_LOOP_HPP_