浏览代码

Add TimerThread

ithewei 4 年之前
父节点
当前提交
324f6f6982
共有 7 个文件被更改,包括 125 次插入21 次删除
  1. 1 0
      Makefile
  2. 1 0
      cpputil/hasync.h
  3. 3 0
      event/hloop.h
  4. 2 2
      evpp/Event.h
  5. 16 19
      evpp/EventLoop.h
  6. 47 0
      evpp/TimerThread.h
  7. 55 0
      evpp/TimerThread_test.cpp

+ 1 - 0
Makefile

@@ -213,6 +213,7 @@ evpp: prepare libhv
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoop_test           evpp/EventLoop_test.cpp           -Llib -lhv -pthread
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoopThread_test     evpp/EventLoopThread_test.cpp     -Llib -lhv -pthread
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/EventLoopThreadPool_test evpp/EventLoopThreadPool_test.cpp -Llib -lhv -pthread
+	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TimerThread_test         evpp/TimerThread_test.cpp         -Llib -lhv -pthread
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TcpServer_test           evpp/TcpServer_test.cpp           -Llib -lhv -pthread
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/TcpClient_test           evpp/TcpClient_test.cpp           -Llib -lhv -pthread
 	$(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Icpputil -Ievpp -o bin/UdpServer_test           evpp/UdpServer_test.cpp           -Llib -lhv -pthread

+ 1 - 0
cpputil/hasync.h

@@ -11,6 +11,7 @@ class HV_EXPORT GlobalThreadPool : public HThreadPool {
     SINGLETON_DECL(GlobalThreadPool)
 protected:
     GlobalThreadPool() : HThreadPool() {}
+    ~GlobalThreadPool() {}
 };
 
 /*

+ 3 - 0
event/hloop.h

@@ -73,12 +73,15 @@ struct hevent_s {
     HEVENT_FIELDS
 };
 
+#define hevent_set_id(ev, id)           ((hevent_t*)(ev))->event_id = id
+#define hevent_set_cb(ev, cb)           ((hevent_t*)(ev))->cb = cb
 #define hevent_set_priority(ev, prio)   ((hevent_t*)(ev))->priority = prio
 #define hevent_set_userdata(ev, udata)  ((hevent_t*)(ev))->userdata = (void*)udata
 
 #define hevent_loop(ev)         (((hevent_t*)(ev))->loop)
 #define hevent_type(ev)         (((hevent_t*)(ev))->event_type)
 #define hevent_id(ev)           (((hevent_t*)(ev))->event_id)
+#define hevent_cb(ev)           (((hevent_t*)(ev))->cb)
 #define hevent_priority(ev)     (((hevent_t*)(ev))->priority)
 #define hevent_userdata(ev)     (((hevent_t*)(ev))->userdata)
 

+ 2 - 2
evpp/Event.h

@@ -30,9 +30,9 @@ struct Event {
 struct Timer {
     htimer_t*       timer;
     TimerCallback   cb;
-    int             repeat;
+    uint32_t        repeat;
 
-    Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, int repeat = INFINITE) {
+    Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, uint32_t repeat = INFINITE) {
         this->timer = timer;
         this->cb = std::move(cb);
         this->repeat = repeat;

+ 16 - 19
evpp/EventLoop.h

@@ -83,15 +83,18 @@ public:
     }
 
     // Timer interfaces: setTimer, killTimer, resetTimer
-    TimerID setTimer(int timeout_ms, TimerCallback cb, int repeat = INFINITE) {
+    TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE, TimerID timerID = INVALID_TIMER_ID) {
         if (loop_ == NULL) return INVALID_TIMER_ID;
         htimer_t* htimer = htimer_add(loop_, onTimer, timeout_ms, repeat);
+        if (timerID == INVALID_TIMER_ID) {
+            timerID = hevent_id(htimer);
+        } else {
+            hevent_set_id(htimer, timerID);
+        }
 
         Timer timer(htimer, cb, repeat);
         hevent_set_userdata(htimer, this);
 
-        TimerID timerID = hevent_id(htimer);
-
         mutex_.lock();
         timers[timerID] = timer;
         mutex_.unlock();
@@ -175,31 +178,25 @@ private:
         EventLoop* loop = (EventLoop*)hevent_userdata(htimer);
 
         TimerID timerID = hevent_id(htimer);
-        TimerCallback cb = NULL;
+        Timer* timer = NULL;
 
         loop->mutex_.lock();
         auto iter = loop->timers.find(timerID);
         if (iter != loop->timers.end()) {
-            Timer& timer = iter->second;
-            cb = timer.cb;
-            --timer.repeat;
+            timer = &iter->second;
+            if (timer->repeat != INFINITE) --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) {
+        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(iter);
+                loop->mutex_.lock();
+                loop->timers.erase(timerID);
+                loop->mutex_.unlock();
             }
         }
-        loop->mutex_.unlock();
     }
 
     static void onCustomEvent(hevent_t* hev) {
@@ -229,7 +226,7 @@ static inline EventLoop* tlsEventLoop() {
 }
 #define currentThreadEventLoop tlsEventLoop()
 
-static inline TimerID setTimer(int timeout_ms, TimerCallback cb, int repeat = INFINITE) {
+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;

+ 47 - 0
evpp/TimerThread.h

@@ -0,0 +1,47 @@
+#ifndef HV_TIMER_THREAD_HPP_
+#define HV_TIMER_THREAD_HPP_
+
+#include "EventLoopThread.h"
+
+namespace hv {
+
+class TimerThread : public EventLoopThread {
+public:
+    std::atomic<TimerID> nextTimerID;
+    TimerThread() : EventLoopThread() {
+        nextTimerID = 0;
+        start();
+    }
+
+    virtual ~TimerThread() {
+        stop();
+        join();
+    }
+
+public:
+    TimerID setTimer(int timeout_ms, TimerCallback cb, uint32_t repeat = INFINITE) {
+        printf("TimerThread::setTimer\n");
+        TimerID timerID = ++nextTimerID;
+        loop()->runInLoop(std::bind(&EventLoop::setTimer, loop(), timeout_ms, cb, repeat, timerID));
+        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) {
+        loop()->runInLoop(std::bind(&EventLoop::killTimer, loop(), timerID));
+    }
+
+    void resetTimer(TimerID timerID) {
+        loop()->runInLoop(std::bind(&EventLoop::resetTimer, loop(), timerID));
+    }
+};
+
+} // end namespace hv
+
+#endif // HV_TIMER_THREAD_HPP_

+ 55 - 0
evpp/TimerThread_test.cpp

@@ -0,0 +1,55 @@
+/*
+ * TimerThread_test.cpp
+ *
+ * @build: make evpp
+ *
+ */
+
+#include "TimerThread.h"
+#include "singleton.h"
+
+namespace hv {
+
+class GlobalTimerThread : public TimerThread {
+    SINGLETON_DECL(GlobalTimerThread)
+protected:
+    GlobalTimerThread() : TimerThread() {}
+    ~GlobalTimerThread() {}
+
+public:
+    static TimerID setTimeout(int timeout_ms, TimerCallback cb) {
+        return GlobalTimerThread::instance()->setTimer(timeout_ms, cb, 1);
+    }
+
+    static void clearTimeout(TimerID timerID) {
+        GlobalTimerThread::instance()->killTimer(timerID);
+    }
+
+    static TimerID setInterval(int interval_ms, TimerCallback cb) {
+        return GlobalTimerThread::instance()->setTimer(interval_ms, cb, INFINITE);
+    }
+
+    static void clearInterval(TimerID timerID) {
+        GlobalTimerThread::instance()->killTimer(timerID);
+    }
+};
+
+SINGLETON_IMPL(GlobalTimerThread)
+
+} // end namespace hv
+
+int main(int argc, char* argv[]) {
+    hv::GlobalTimerThread::setTimeout(3000, [](hv::TimerID timerID) {
+        printf("setTimeou timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL));
+    });
+
+    hv::GlobalTimerThread::setInterval(1000, [](hv::TimerID timerID) {
+        printf("setInterval timerID=%lu time=%lus\n", (unsigned long)timerID, (unsigned long)time(NULL));
+    });
+
+    // press Enter to stop
+    while (getchar() != '\n');
+
+    hv::GlobalTimerThread::exitInstance();
+    return 0;
+}