Browse Source

support signal event for (#423)(#458)

ithewei 1 year ago
parent
commit
6ad0d72530
8 changed files with 89 additions and 5 deletions
  1. 1 1
      README-CN.md
  2. 1 1
      README.md
  3. 2 0
      docs/API.md
  4. 7 1
      docs/cn/hloop.md
  5. 4 0
      event/hevent.h
  6. 57 0
      event/hloop.c
  7. 9 2
      event/hloop.h
  8. 8 0
      examples/hloop_test.c

+ 1 - 1
README-CN.md

@@ -52,7 +52,7 @@
 ## ✨ 特性
 
 - 跨平台(Linux, Windows, macOS, Android, iOS, BSD, Solaris)
-- 高性能事件循环(网络IO事件、定时器事件、空闲事件、自定义事件)
+- 高性能事件循环(网络IO事件、定时器事件、空闲事件、自定义事件、信号
 - TCP/UDP服务端/客户端/代理
 - TCP支持心跳、重连、转发、多线程安全write和close等特性
 - 内置常见的拆包模式(固定包长、分界符、头部长度字段)

+ 1 - 1
README.md

@@ -28,7 +28,7 @@ but simpler api and richer protocols.
 ## ✨ Features
 
 - Cross-platform (Linux, Windows, macOS, Android, iOS, BSD, Solaris)
-- High-performance EventLoop (IO, timer, idle, custom)
+- High-performance EventLoop (IO, timer, idle, custom, signal)
 - TCP/UDP client/server/proxy
 - TCP supports heartbeat, reconnect, upstream, MultiThread-safe write and close, etc.
 - Built-in common unpacking modes (FixedLength, Delimiter, LengthField)

+ 2 - 0
docs/API.md

@@ -479,6 +479,8 @@
 - htimer_reset
 - hidle_add
 - hidle_del
+- hsignal_add
+- hsignal_del
 
 ### nlog.h
 - network_logger

+ 7 - 1
docs/cn/hloop.md

@@ -43,7 +43,7 @@ struct hevent_s {
 // 获取事件用户数据
 #define hevent_userdata(ev)     (((hevent_t*)(ev))->userdata)
 
-// hidle_t、htimer_t、hio_t皆是继承自hevent_t,继承上面的数据成员和函数方法
+// hio_t、htimer_t、hsignal_t、hidle_t皆是继承自hevent_t,继承上面的数据成员和函数方法
 
 // 新建事件循环
 hloop_t* hloop_new(int flags DEFAULT(HLOOP_FLAG_AUTO_FREE));
@@ -107,6 +107,12 @@ void* hloop_userdata(hloop_t* loop);
 // 投递事件
 void hloop_post_event(hloop_t* loop, hevent_t* ev);
 
+// 添加信号处理
+hsignal_t* hsignal_add(hloop_t* loop, hsignal_cb cb, int signo);
+
+// 删除信号处理
+void       hsignal_del(hsignal_t* sig);
+
 // 添加空闲事件
 hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
 

+ 4 - 0
event/hevent.h

@@ -25,6 +25,7 @@
 #define HIO_READ_UNTIL_DELIM    0x4
 
 ARRAY_DECL(hio_t*, io_array);
+ARRAY_DECL(hsignal_t*, signal_array);
 QUEUE_DECL(hevent_t, event_queue);
 
 struct hloop_s {
@@ -45,6 +46,9 @@ struct hloop_s {
     uint32_t                    npendings;
     // pendings: with priority as array.index
     hevent_t*                   pendings[HEVENT_PRIORITY_SIZE];
+    // signals
+    struct signal_array         signals;
+    uint32_t                    nsignals;
     // idles
     struct list_head            idles;
     uint32_t                    nidles;

+ 57 - 0
event/hloop.c

@@ -394,6 +394,14 @@ static void hloop_cleanup(hloop_t* loop) {
     }
     heap_init(&loop->realtimers, NULL);
 
+    // signals
+    printd("cleanup signals...\n");
+    for (int i = 0; i < loop->signals.maxsize; ++i) {
+        hsignal_t* sig = loop->signals.ptr[i];
+        HV_FREE(sig);
+    }
+    signal_array_cleanup(&loop->signals);
+
     // readbuf
     if (loop->readbuf.base && loop->readbuf.len) {
         HV_FREE(loop->readbuf.base);
@@ -587,6 +595,55 @@ void* hloop_userdata(hloop_t* loop) {
     return loop->userdata;
 }
 
+static hloop_t* s_signal_loop = NULL;
+static void signal_handler(int signo) {
+    if (!s_signal_loop) return;
+    if (signo >= s_signal_loop->signals.maxsize) return;
+    hsignal_t* sig = s_signal_loop->signals.ptr[signo];
+    if (!sig) return;
+    hloop_post_event(s_signal_loop, sig);
+}
+
+hsignal_t* hsignal_add(hloop_t* loop, hsignal_cb cb, int signo) {
+    int max_signo = 64;
+#ifdef _NSIG
+    max_signo = _NSIG;
+#endif
+    if (signo <= 0 || signo >= max_signo) {
+        hloge("signo %d over %d!", signo, max_signo);
+        return NULL;
+    }
+    if (loop->signals.maxsize == 0) {
+        signal_array_init(&loop->signals, max_signo);
+    }
+    hsignal_t* sig = loop->signals.ptr[signo];
+    if (sig == NULL) {
+        HV_ALLOC_SIZEOF(sig);
+        sig->loop = loop;
+        sig->event_type = HEVENT_TYPE_SIGNAL;
+        // NOTE: use event_id as signo
+        sig->event_id = signo;
+        sig->cb = cb;
+        sig->priority = HEVENT_HIGHEST_PRIORITY;
+        loop->signals.ptr[signo] = sig;
+        loop->nsignals++;
+    }
+    EVENT_ACTIVE(sig);
+    s_signal_loop = loop;
+    signal(signo, signal_handler);
+    return sig;
+}
+
+void hsignal_del(hsignal_t* sig) {
+    if (!sig->active) return;
+    hloop_t* loop = sig->loop;
+    int signo = (int)sig->event_id;
+    if (signo >= loop->signals.maxsize) return;
+    loop->signals.ptr[signo] = NULL;
+    loop->nsignals--;
+    EVENT_DEL(sig);
+}
+
 hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat) {
     hidle_t* idle;
     HV_ALLOC_SIZEOF(idle);

+ 9 - 2
event/hloop.h

@@ -11,16 +11,18 @@ typedef struct hevent_s     hevent_t;
 
 // NOTE: The following structures are subclasses of hevent_t,
 // inheriting hevent_t data members and function members.
+typedef struct hio_s        hio_t;
 typedef struct hidle_s      hidle_t;
 typedef struct htimer_s     htimer_t;
 typedef struct htimeout_s   htimeout_t;
 typedef struct hperiod_s    hperiod_t;
-typedef struct hio_s        hio_t;
+typedef struct hevent_s     hsignal_t;
 
 typedef void (*hevent_cb)   (hevent_t* ev);
+typedef void (*hio_cb)      (hio_t* io);
 typedef void (*hidle_cb)    (hidle_t* idle);
 typedef void (*htimer_cb)   (htimer_t* timer);
-typedef void (*hio_cb)      (hio_t* io);
+typedef void (*hsignal_cb)  (hsignal_t* sig);
 
 typedef void (*haccept_cb)  (hio_t* io);
 typedef void (*hconnect_cb) (hio_t* io);
@@ -42,6 +44,7 @@ typedef enum {
     HEVENT_TYPE_PERIOD  = 0x00000020,
     HEVENT_TYPE_TIMER   = HEVENT_TYPE_TIMEOUT|HEVENT_TYPE_PERIOD,
     HEVENT_TYPE_IDLE    = 0x00000100,
+    HEVENT_TYPE_SIGNAL  = 0x00000200,
     HEVENT_TYPE_CUSTOM  = 0x00000400, // 1024
 } hevent_type_e;
 
@@ -184,6 +187,10 @@ HV_EXPORT void* hloop_userdata(hloop_t* loop);
 // NOTE: hloop_post_event is thread-safe, used to post event from other thread to loop thread.
 HV_EXPORT void hloop_post_event(hloop_t* loop, hevent_t* ev);
 
+// signal
+HV_EXPORT hsignal_t* hsignal_add(hloop_t* loop, hsignal_cb cb, int signo);
+HV_EXPORT void       hsignal_del(hsignal_t* sig);
+
 // idle
 HV_EXPORT hidle_t* hidle_add(hloop_t* loop, hidle_cb cb, uint32_t repeat DEFAULT(INFINITE));
 HV_EXPORT void     hidle_del(hidle_t* idle);

+ 8 - 0
examples/hloop_test.c

@@ -64,6 +64,11 @@ void on_custom_events(hevent_t* ev) {
     printf("on_custom_events event_type=%d userdata=%ld\n", (int)ev->event_type, (long)(intptr_t)ev->userdata);
 }
 
+void on_signal(hsignal_t* sig) {
+    printf("on_signal signo=%d\n", (int)hevent_id(sig));
+    hloop_stop(hevent_loop(sig));
+}
+
 int main() {
     // memcheck atexit
     HV_MEMCHECK;
@@ -87,6 +92,9 @@ int main() {
     htimer_add_period(loop, cron_minutely, -1, -1, -1, -1, -1, INFINITE);
     htimer_add_period(loop, cron_hourly, minute+1, -1, -1, -1, -1, INFINITE);
 
+    // test signal: enter Ctrl-C to trigger
+    hsignal_add(loop, on_signal, SIGINT);
+
     // test network_logger
     htimer_add(loop, timer_write_log, 1000, INFINITE);
     hlog_set_handler(mylogger);