ithewei 5 lat temu
rodzic
commit
30de935189
4 zmienionych plików z 309 dodań i 0 usunięć
  1. 46 0
      evpp/Event.h
  2. 184 0
      evpp/EventLoop.h
  3. 56 0
      evpp/EventLoop_test.cpp
  4. 23 0
      evpp/README.md

+ 46 - 0
evpp/Event.h

@@ -0,0 +1,46 @@
+#ifndef HV_EVENT_HPP_
+#define HV_EVENT_HPP_
+
+#include <functional>
+#include <memory>
+
+#include "hloop.h"
+
+namespace hv {
+
+struct Event;
+struct Timer;
+
+typedef uint64_t    TimerID;
+
+typedef std::function<void(Event*)>     EventCallback;
+typedef std::function<void(TimerID)>    TimerCallback;
+
+struct Event {
+    hevent_t        event;
+    EventCallback   cb;
+
+    Event(EventCallback cb = NULL) {
+        memset(&event, 0, sizeof(hevent_t));
+        this->cb = cb;
+    }
+};
+
+struct Timer {
+    htimer_t*       timer;
+    TimerCallback   cb;
+    int             repeat;
+
+    Timer(htimer_t* timer = NULL, TimerCallback cb = NULL, int repeat = INFINITE) {
+        this->timer = timer;
+        this->cb = cb;
+        this->repeat = repeat;
+    }
+};
+
+typedef std::shared_ptr<Event> EventPtr;
+typedef std::shared_ptr<Timer> TimerPtr;
+
+}
+
+#endif // HV_EVENT_HPP_

+ 184 - 0
evpp/EventLoop.h

@@ -0,0 +1,184 @@
+#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 "Event.h"
+
+namespace hv {
+
+class EventLoop {
+public:
+    typedef std::function<void()> 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<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;
+            }
+        }
+    }
+
+    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<EventPtr>        customEvents;   // GUAREDE_BY(mutex_)
+    std::map<TimerID, Timer>    timers;         // GUAREDE_BY(mutex_)
+};
+
+}
+
+#endif // HV_EVENT_LOOP_HPP_

+ 56 - 0
evpp/EventLoop_test.cpp

@@ -0,0 +1,56 @@
+/*
+ * EventLoop_test.cpp
+ *
+ * @build
+ * make libhv && sudo make install
+ * g++ -std=c++11 EventLoop_test.cpp -o EventLoop_test -I/usr/local/include/hv -lhv
+ *
+ */
+
+#include "hbase.h" // import HV_MEMCHECK
+
+#include "EventLoop.h"
+
+using namespace hv;
+
+static void onTimer(TimerID timerID, EventLoop* loop, int n) {
+    static int cnt = 0;
+    printf("n=%d timerID=%lu time=%lus\n", n, (unsigned long)timerID, (unsigned long)time(NULL));
+    if (++cnt == 15) {
+        printf("killTimer(%ld)\n", (unsigned long)timerID);
+        loop->killTimer(timerID);
+    }
+}
+
+int main(int argc, char* argv[]) {
+    HV_MEMCHECK;
+
+    EventLoop loop;
+
+    int n = 100;
+    loop.setInterval(1000, std::bind(onTimer, std::placeholders::_1, &loop, n));
+
+    loop.setTimeout(10000, [&loop](TimerID timerID){
+        static int cnt = 0;
+        if (cnt++ == 0) {
+            printf("resetTimer(%ld)\n", (unsigned long)timerID);
+            loop.resetTimer(timerID);
+        } else {
+            loop.stop();
+        }
+    });
+
+    printf("tid=%ld\n", hv_gettid());
+
+    loop.queueInLoop([](){
+        printf("queueInLoop tid=%ld\n", hv_gettid());
+    });
+
+    loop.runInLoop([](){
+        printf("runInLoop tid=%ld\n", hv_gettid());
+    });
+
+    loop.start();
+
+    return 0;
+}

+ 23 - 0
evpp/README.md

@@ -0,0 +1,23 @@
+The evpp module is designed to be header-only and does not participate in compilation.
+hloop.h is encapsulated into c++ classes, referring to muduo and evpp.
+You can modify and use evpp classes according to your own business.
+
+evpp模块被设计成只包含头文件,不参与编译。
+hloop.h中的c接口被封装成了c++的类,参考了muduo和evpp。
+你能修改和使用这些类根据你自己的业务。
+
+## 目录结构
+
+```
+.
+├── Channel.h               IO管道类,封装了hio_t
+├── Event.h                 事件类,封装了hevent_t、htimer_t
+├── EventLoop.h             事件循环类,封装了hloop_t
+├── EventLoopThread.h       事件循环线程类,组合了EventLoop和thread
+├── EventLoopThreadPool.h   事件循环线程池类,组合了EventLoop和ThreadPool
+├── TcpClient.h             TCP客户端类
+├── TcpServer.h             TCP服务端类
+├── UdpClient.h             UDP客户端类
+└── UdpServer.h             UDP服务端类
+
+```