From b1b69ac7c1502ec73f5da17c82150186174bf5c9 Mon Sep 17 00:00:00 2001 From: Diego Giagio Date: Fri, 17 Jan 2014 23:20:42 -0200 Subject: [PATCH] Implemented EV_CLOSED event for epoll backend (EPOLLRDHUP). - Added new EV_CLOSED event - detects premature connection close by clients without the necessity of reading all the pending data. Does not depend on EV_READ and/or EV_WRITE. - Added new EV_FEATURE_EARLY_CLOSED feature for epoll. Must be supported for listening to EV_CLOSED event. - Added new regression test: test-closed.c - All regression tests passed (test/regress and test/test.sh) - strace output of test-closed using EV_CLOSED: socketpair(PF_LOCAL, SOCK_STREAM, 0, [6, 7]) = 0 sendto(6, "test string\0", 12, 0, NULL, 0) = 12 shutdown(6, SHUT_WR) = 0 epoll_ctl(3, EPOLL_CTL_ADD, 7, {EPOLLRDHUP, {u32=7, u64=7}}) = 0 epoll_wait(3, {{EPOLLRDHUP, {u32=7, u64=7}}}, 32, 3000) = 1 epoll_ctl(3, EPOLL_CTL_MOD, 7, {EPOLLRDHUP, {u32=7, u64=7}}) = 0 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYM... write(1, "closed_cb: detected connection close "..., 45) = 45 --- changelist-internal.h | 1 + epoll.c | 1149 +++++++++++++++++++++++++++++++++++++--- event.c | 34 +- evmap.c | 46 +- include/event2/event.h | 10 +- make_epoll_table.py | 66 +-- test/Makefile.nmake | 6 +- test/include.am | 3 + test/test-closed.c | 117 ++++ test/test.sh | 2 +- 10 files changed, 1297 insertions(+), 137 deletions(-) create mode 100644 test/test-closed.c diff --git a/changelist-internal.h b/changelist-internal.h index 0ec2eeafff..98fc52aebf 100644 --- a/changelist-internal.h +++ b/changelist-internal.h @@ -62,6 +62,7 @@ struct event_change { * and write_change is unused. */ ev_uint8_t read_change; ev_uint8_t write_change; + ev_uint8_t close_change; }; /* Flags for read_change and write_change. */ diff --git a/epoll.c b/epoll.c index 2d07ff6231..bd425ed516 100644 --- a/epoll.c +++ b/epoll.c @@ -71,6 +71,13 @@ #define USING_TIMERFD #endif +/* Since Linux 2.6.17, epoll is able to report about peer half-closed connection + using special EPOLLRDHUP flag on a read event. +*/ +#if !defined(EPOLLRDHUP) +#define EPOLLRDHUP 0 +#endif + struct epollop { struct epoll_event *events; int nevents; @@ -92,7 +99,7 @@ static const struct eventop epollops_changelist = { epoll_dispatch, epoll_dealloc, 1, /* need reinit */ - EV_FEATURE_ET|EV_FEATURE_O1, + EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE, EVENT_CHANGELIST_FDINFO_SIZE }; @@ -110,7 +117,7 @@ const struct eventop epollops = { epoll_dispatch, epoll_dealloc, 1, /* need reinit */ - EV_FEATURE_ET|EV_FEATURE_O1, + EV_FEATURE_ET|EV_FEATURE_O1|EV_FEATURE_EARLY_CLOSE, 0 }; @@ -241,7 +248,7 @@ epoll_op_to_string(int op) Note also that this table is a little sparse, since ADD+DEL is nonsensical ("xxx" in the list below.) - Note also also that we are shifting old_events by only 3 bits, since + Note also also that we are shifting old_events by only 5 bits, since EV_READ is 2 and EV_WRITE is 4. The table was auto-generated with a python script, according to this @@ -312,18 +319,26 @@ epoll_op_to_string(int op) EV_CHANGE_DEL==2 EV_READ ==2 EV_WRITE ==4 - Bit 0: read change is add - Bit 1: read change is del - Bit 2: write change is add - Bit 3: write change is del - Bit 4: old events had EV_READ - Bit 5: old events had EV_WRITE + EV_CLOSED ==0x80 + + Bit 0: close change is add + Bit 1: close change is del + Bit 2: read change is add + Bit 3: read change is del + Bit 4: write change is add + Bit 5: write change is del + Bit 6: old events had EV_READ + Bit 7: old events had EV_WRITE + Bit 8: old events had EV_CLOSED */ #define INDEX(c) \ - ( (((c)->read_change&(EV_CHANGE_ADD|EV_CHANGE_DEL))) | \ - (((c)->write_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 2) | \ - (((c)->old_events&(EV_READ|EV_WRITE)) << 3) ) + ( (((c)->close_change&(EV_CHANGE_ADD|EV_CHANGE_DEL))) | \ + (((c)->read_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 2) | \ + (((c)->write_change&(EV_CHANGE_ADD|EV_CHANGE_DEL)) << 4) | \ + (((c)->old_events&(EV_READ|EV_WRITE)) << 5) | \ + (((c)->old_events&(EV_CLOSED)) << 1) \ + ) #if EV_READ != 2 || EV_WRITE != 4 || EV_CHANGE_ADD != 1 || EV_CHANGE_DEL != 2 #error "Libevent's internals changed! Regenerate the op_table in epoll.c" @@ -333,70 +348,1030 @@ static const struct operation { int events; int op; } op_table[] = { - { 0, 0 }, /* old= 0, write: 0, read: 0 */ - { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write: 0, read:add */ - { EPOLLIN, EPOLL_CTL_DEL }, /* old= 0, write: 0, read:del */ - { 0, -1 }, /* old= 0, write: 0, read:xxx */ - { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD },/* old= 0, write:add, read:add */ - { EPOLLOUT, EPOLL_CTL_ADD }, /* old= 0, write:add, read:del */ - { 0, -1 }, /* old= 0, write:add, read:xxx */ - { EPOLLOUT, EPOLL_CTL_DEL }, /* old= 0, write:del, read: 0 */ - { EPOLLIN, EPOLL_CTL_ADD }, /* old= 0, write:del, read:add */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= 0, write:del, read:del */ - { 0, -1 }, /* old= 0, write:del, read:xxx */ - { 0, -1 }, /* old= 0, write:xxx, read: 0 */ - { 0, -1 }, /* old= 0, write:xxx, read:add */ - { 0, -1 }, /* old= 0, write:xxx, read:del */ - { 0, -1 }, /* old= 0, write:xxx, read:xxx */ - { 0, 0 }, /* old= r, write: 0, read: 0 */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write: 0, read:add */ - { EPOLLIN, EPOLL_CTL_DEL }, /* old= r, write: 0, read:del */ - { 0, -1 }, /* old= r, write: 0, read:xxx */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= r, write:add, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= r, write:add, read:add */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old= r, write:add, read:del */ - { 0, -1 }, /* old= r, write:add, read:xxx */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read: 0 */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old= r, write:del, read:add */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= r, write:del, read:del */ - { 0, -1 }, /* old= r, write:del, read:xxx */ - { 0, -1 }, /* old= r, write:xxx, read: 0 */ - { 0, -1 }, /* old= r, write:xxx, read:add */ - { 0, -1 }, /* old= r, write:xxx, read:del */ - { 0, -1 }, /* old= r, write:xxx, read:xxx */ - { 0, 0 }, /* old= w, write: 0, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= w, write: 0, read:add */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write: 0, read:del */ - { 0, -1 }, /* old= w, write: 0, read:xxx */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old= w, write:add, read:add */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old= w, write:add, read:del */ - { 0, -1 }, /* old= w, write:add, read:xxx */ - { EPOLLOUT, EPOLL_CTL_DEL }, /* old= w, write:del, read: 0 */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old= w, write:del, read:add */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old= w, write:del, read:del */ - { 0, -1 }, /* old= w, write:del, read:xxx */ - { 0, -1 }, /* old= w, write:xxx, read: 0 */ - { 0, -1 }, /* old= w, write:xxx, read:add */ - { 0, -1 }, /* old= w, write:xxx, read:del */ - { 0, -1 }, /* old= w, write:xxx, read:xxx */ - { 0, 0 }, /* old=rw, write: 0, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write: 0, read:add */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old=rw, write: 0, read:del */ - { 0, -1 }, /* old=rw, write: 0, read:xxx */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write:add, read: 0 */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD },/* old=rw, write:add, read:add */ - { EPOLLOUT, EPOLL_CTL_MOD }, /* old=rw, write:add, read:del */ - { 0, -1 }, /* old=rw, write:add, read:xxx */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old=rw, write:del, read: 0 */ - { EPOLLIN, EPOLL_CTL_MOD }, /* old=rw, write:del, read:add */ - { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL },/* old=rw, write:del, read:del */ - { 0, -1 }, /* old=rw, write:del, read:xxx */ - { 0, -1 }, /* old=rw, write:xxx, read: 0 */ - { 0, -1 }, /* old=rw, write:xxx, read:add */ - { 0, -1 }, /* old=rw, write:xxx, read:del */ - { 0, -1 }, /* old=rw, write:xxx, read:xxx */ + /* old= 0, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= 0, write: 0, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write: 0, read: 0, close:del */ + { EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= 0, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= 0, write: 0, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_ADD }, + /* old= 0, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write: 0, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_ADD }, + /* old= 0, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= 0, write: 0, read:del, close: 0 */ + { EPOLLIN, EPOLL_CTL_DEL }, + /* old= 0, write: 0, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write: 0, read:del, close:del */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= 0, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= 0, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= 0, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= 0, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= 0, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= 0, write:add, read: 0, close: 0 */ + { EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:add, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= 0, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= 0, write:add, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_ADD }, + /* old= 0, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= 0, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= 0, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= 0, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= 0, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= 0, write:del, read: 0, close: 0 */ + { EPOLLOUT, EPOLL_CTL_DEL }, + /* old= 0, write:del, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:del, read: 0, close:del */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= 0, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= 0, write:del, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_ADD }, + /* old= 0, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_ADD }, + /* old= 0, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= 0, write:del, read:del, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, + /* old= 0, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_ADD }, + /* old= 0, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= 0, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= 0, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= 0, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= 0, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= 0, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= 0, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= 0, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= 0, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= 0, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= 0, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= 0, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= 0, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= 0, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= 0, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= 0, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= 0, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= 0, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= 0, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= 0, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= 0, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= 0, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= r, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= r, write: 0, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write: 0, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= r, write: 0, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write: 0, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= r, write: 0, read:del, close: 0 */ + { EPOLLIN, EPOLL_CTL_DEL }, + /* old= r, write: 0, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write: 0, read:del, close:del */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= r, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= r, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= r, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= r, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= r, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= r, write:add, read: 0, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:add, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= r, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= r, write:add, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= r, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= r, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= r, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= r, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= r, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= r, write:del, read: 0, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write:del, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:del, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= r, write:del, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= r, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= r, write:del, read:del, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, + /* old= r, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= r, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= r, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= r, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= r, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= r, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= r, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= r, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= r, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= r, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= r, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= r, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= r, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= r, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= r, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= r, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= r, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= r, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= r, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= r, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= r, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= r, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= r, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= w, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= w, write: 0, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write: 0, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= w, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= w, write: 0, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= w, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= w, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= w, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= w, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= w, write:add, read: 0, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:add, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= w, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= w, write:add, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= w, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= w, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= w, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= w, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= w, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= w, write:del, read: 0, close: 0 */ + { EPOLLOUT, EPOLL_CTL_DEL }, + /* old= w, write:del, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:del, read: 0, close:del */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= w, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= w, write:del, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= w, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= w, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= w, write:del, read:del, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, + /* old= w, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= w, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= w, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= w, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= w, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= w, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= w, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= w, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= w, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= w, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= w, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= w, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= w, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= w, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= w, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= w, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= w, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= w, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= w, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= w, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= w, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= w, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= w, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= rw, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= rw, write: 0, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= rw, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= rw, write: 0, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= rw, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= rw, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= rw, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= rw, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= rw, write:add, read: 0, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:add, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= rw, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= rw, write:add, read:del, close: 0 */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= rw, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= rw, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= rw, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= rw, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= rw, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= rw, write:del, read: 0, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= rw, write:del, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:del, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= rw, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= rw, write:del, read:add, close: 0 */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= rw, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= rw, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= rw, write:del, read:del, close: 0 */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_DEL }, + /* old= rw, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= rw, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= rw, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= rw, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= rw, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= rw, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= rw, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= rw, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= rw, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= rw, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= rw, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= rw, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= rw, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= rw, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= rw, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= rw, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= rw, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= rw, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= rw, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= rw, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= rw, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= rw, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= rw, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= c, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= c, write: 0, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write: 0, read: 0, close:del */ + { EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= c, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= c, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write: 0, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= c, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= c, write: 0, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write: 0, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write: 0, read:del, close:del */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= c, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= c, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= c, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= c, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= c, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= c, write:add, read: 0, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= c, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= c, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= c, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= c, write:add, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= c, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= c, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= c, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= c, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= c, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= c, write:del, read: 0, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read: 0, close:del */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= c, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= c, write:del, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= c, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= c, write:del, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= c, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= c, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= c, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= c, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= c, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= c, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= c, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= c, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= c, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= c, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= c, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= c, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= c, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= c, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= c, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= c, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= c, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= c, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= c, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= c, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= c, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= c, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cr, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= cr, write: 0, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= cr, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= cr, write: 0, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write: 0, read:del, close:del */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= cr, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= cr, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cr, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= cr, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= cr, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cr, write:add, read: 0, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cr, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= cr, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= cr, write:add, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cr, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= cr, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cr, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= cr, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= cr, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cr, write:del, read: 0, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= cr, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= cr, write:del, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= cr, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= cr, write:del, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cr, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= cr, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= cr, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cr, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= cr, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= cr, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cr, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= cr, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= cr, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= cr, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= cr, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= cr, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= cr, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= cr, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= cr, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= cr, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= cr, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= cr, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= cr, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cr, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= cr, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= cr, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cw, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old= cw, write: 0, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old= cw, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old= cw, write: 0, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old= cw, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cw, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old= cw, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old= cw, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cw, write:add, read: 0, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read: 0, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read: 0, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old= cw, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old= cw, write:add, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old= cw, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old= cw, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cw, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old= cw, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old= cw, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cw, write:del, read: 0, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read: 0, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read: 0, close:del */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= cw, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old= cw, write:del, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old= cw, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old= cw, write:del, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old= cw, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old= cw, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old= cw, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cw, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old= cw, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old= cw, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old= cw, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old= cw, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old= cw, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old= cw, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old= cw, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old= cw, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old= cw, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old= cw, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old= cw, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old= cw, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old= cw, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old= cw, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old= cw, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old= cw, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old= cw, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old= cw, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, + /* old=crw, write: 0, read: 0, close: 0 */ + { 0, 0 }, + /* old=crw, write: 0, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read: 0, close:xxx */ + { 0, 255 }, + /* old=crw, write: 0, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:add, close:xxx */ + { 0, 255 }, + /* old=crw, write: 0, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write: 0, read:del, close:xxx */ + { 0, 255 }, + /* old=crw, write: 0, read:xxx, close: 0 */ + { 0, 255 }, + /* old=crw, write: 0, read:xxx, close:add */ + { 0, 255 }, + /* old=crw, write: 0, read:xxx, close:del */ + { 0, 255 }, + /* old=crw, write: 0, read:xxx, close:xxx */ + { 0, 255 }, + /* old=crw, write:add, read: 0, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read: 0, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read: 0, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write:add, read: 0, close:xxx */ + { 0, 255 }, + /* old=crw, write:add, read:add, close: 0 */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:add, close:add */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:add, close:del */ + { EPOLLIN|EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:add, close:xxx */ + { 0, 255 }, + /* old=crw, write:add, read:del, close: 0 */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:del, close:add */ + { EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:del, close:del */ + { EPOLLOUT, EPOLL_CTL_MOD }, + /* old=crw, write:add, read:del, close:xxx */ + { 0, 255 }, + /* old=crw, write:add, read:xxx, close: 0 */ + { 0, 255 }, + /* old=crw, write:add, read:xxx, close:add */ + { 0, 255 }, + /* old=crw, write:add, read:xxx, close:del */ + { 0, 255 }, + /* old=crw, write:add, read:xxx, close:xxx */ + { 0, 255 }, + /* old=crw, write:del, read: 0, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read: 0, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read: 0, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old=crw, write:del, read: 0, close:xxx */ + { 0, 255 }, + /* old=crw, write:del, read:add, close: 0 */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read:add, close:add */ + { EPOLLIN|EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read:add, close:del */ + { EPOLLIN, EPOLL_CTL_MOD }, + /* old=crw, write:del, read:add, close:xxx */ + { 0, 255 }, + /* old=crw, write:del, read:del, close: 0 */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read:del, close:add */ + { EPOLLRDHUP, EPOLL_CTL_MOD }, + /* old=crw, write:del, read:del, close:del */ + { EPOLLIN|EPOLLOUT|EPOLLRDHUP, EPOLL_CTL_DEL }, + /* old=crw, write:del, read:del, close:xxx */ + { 0, 255 }, + /* old=crw, write:del, read:xxx, close: 0 */ + { 0, 255 }, + /* old=crw, write:del, read:xxx, close:add */ + { 0, 255 }, + /* old=crw, write:del, read:xxx, close:del */ + { 0, 255 }, + /* old=crw, write:del, read:xxx, close:xxx */ + { 0, 255 }, + /* old=crw, write:xxx, read: 0, close: 0 */ + { 0, 255 }, + /* old=crw, write:xxx, read: 0, close:add */ + { 0, 255 }, + /* old=crw, write:xxx, read: 0, close:del */ + { 0, 255 }, + /* old=crw, write:xxx, read: 0, close:xxx */ + { 0, 255 }, + /* old=crw, write:xxx, read:add, close: 0 */ + { 0, 255 }, + /* old=crw, write:xxx, read:add, close:add */ + { 0, 255 }, + /* old=crw, write:xxx, read:add, close:del */ + { 0, 255 }, + /* old=crw, write:xxx, read:add, close:xxx */ + { 0, 255 }, + /* old=crw, write:xxx, read:del, close: 0 */ + { 0, 255 }, + /* old=crw, write:xxx, read:del, close:add */ + { 0, 255 }, + /* old=crw, write:xxx, read:del, close:del */ + { 0, 255 }, + /* old=crw, write:xxx, read:del, close:xxx */ + { 0, 255 }, + /* old=crw, write:xxx, read:xxx, close: 0 */ + { 0, 255 }, + /* old=crw, write:xxx, read:xxx, close:add */ + { 0, 255 }, + /* old=crw, write:xxx, read:xxx, close:del */ + { 0, 255 }, + /* old=crw, write:xxx, read:xxx, close:xxx */ + { 0, 255 }, }; static int @@ -424,13 +1399,14 @@ epoll_apply_one_change(struct event_base *base, epev.data.fd = ch->fd; epev.events = events; if (epoll_ctl(epollop->epfd, op, ch->fd, &epev) == 0) { - event_debug(("Epoll %s(%d) on fd %d okay. [old events were %d; read change was %d; write change was %d]", + event_debug(("Epoll %s(%d) on fd %d okay. [old events were %d; read change was %d; write change was %d; close change was %d]", epoll_op_to_string(op), (int)epev.events, (int)ch->fd, ch->old_events, ch->read_change, - ch->write_change)); + ch->write_change, + ch->close_change)); return 0; } @@ -490,7 +1466,7 @@ epoll_apply_one_change(struct event_base *base, break; } - event_warn("Epoll %s(%d) on fd %d failed. Old events were %d; read change was %d (%s); write change was %d (%s)", + event_warn("Epoll %s(%d) on fd %d failed. Old events were %d; read change was %d (%s); write change was %d (%s); close change was %d (%s)", epoll_op_to_string(op), (int)epev.events, ch->fd, @@ -498,7 +1474,9 @@ epoll_apply_one_change(struct event_base *base, ch->read_change, change_to_string(ch->read_change), ch->write_change, - change_to_string(ch->write_change)); + change_to_string(ch->write_change), + ch->close_change, + change_to_string(ch->close_change)); return -1; } @@ -529,13 +1507,16 @@ epoll_nochangelist_add(struct event_base *base, evutil_socket_t fd, struct event_change ch; ch.fd = fd; ch.old_events = old; - ch.read_change = ch.write_change = 0; + ch.read_change = ch.write_change = ch.close_change = 0; if (events & EV_WRITE) ch.write_change = EV_CHANGE_ADD | (events & EV_ET); if (events & EV_READ) ch.read_change = EV_CHANGE_ADD | (events & EV_ET); + if (events & EV_CLOSED) + ch.close_change = EV_CHANGE_ADD | + (events & EV_ET); return epoll_apply_one_change(base, base->evbase, &ch); } @@ -547,11 +1528,13 @@ epoll_nochangelist_del(struct event_base *base, evutil_socket_t fd, struct event_change ch; ch.fd = fd; ch.old_events = old; - ch.read_change = ch.write_change = 0; + ch.read_change = ch.write_change = ch.close_change = 0; if (events & EV_WRITE) ch.write_change = EV_CHANGE_DEL; if (events & EV_READ) ch.read_change = EV_CHANGE_DEL; + if (events & EV_CLOSED) + ch.close_change = EV_CHANGE_DEL; return epoll_apply_one_change(base, base->evbase, &ch); } @@ -636,6 +1619,8 @@ epoll_dispatch(struct event_base *base, struct timeval *tv) ev |= EV_READ; if (what & EPOLLOUT) ev |= EV_WRITE; + if (what & EPOLLRDHUP) + ev |= EV_CLOSED; } if (!ev) diff --git a/event.c b/event.c index eea2c0fcde..c946cbf98a 100644 --- a/event.c +++ b/event.c @@ -1526,10 +1526,11 @@ event_process_active_single_queue(struct event_base *base, else event_del_nolock_(ev, EVENT_DEL_NOBLOCK); event_debug(( - "event_process_active: event: %p, %s%scall %p", + "event_process_active: event: %p, %s%s%scall %p", ev, ev->ev_res & EV_READ ? "EV_READ " : " ", ev->ev_res & EV_WRITE ? "EV_WRITE " : " ", + ev->ev_res & EV_CLOSED ? "EV_CLOSED " : " ", ev->ev_callback)); } else { event_queue_remove_active(base, evcb); @@ -1931,7 +1932,7 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events, eonce->cb = callback; eonce->arg = arg; - if ((events & (EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE)) == EV_TIMEOUT) { + if ((events & (EV_TIMEOUT|EV_SIGNAL|EV_READ|EV_WRITE|EV_CLOSED)) == EV_TIMEOUT) { evtimer_assign(&eonce->ev, base, event_once_cb, eonce); if (tv == NULL || ! evutil_timerisset(tv)) { @@ -1941,8 +1942,8 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events, * it fast (and order-preserving). */ activate = 1; } - } else if (events & (EV_READ|EV_WRITE)) { - events &= EV_READ|EV_WRITE; + } else if (events & (EV_READ|EV_WRITE|EV_CLOSED)) { + events &= EV_READ|EV_WRITE|EV_CLOSED; event_assign(&eonce->ev, base, fd, events, event_once_cb, eonce); } else { @@ -1992,9 +1993,9 @@ event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, shor ev->ev_pncalls = NULL; if (events & EV_SIGNAL) { - if ((events & (EV_READ|EV_WRITE)) != 0) { + if ((events & (EV_READ|EV_WRITE|EV_CLOSED)) != 0) { event_warnx("%s: EV_SIGNAL is not compatible with " - "EV_READ or EV_WRITE", __func__); + "EV_READ, EV_WRITE or EV_CLOSED", __func__); return -1; } ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL; @@ -2244,13 +2245,13 @@ event_pending(const struct event *ev, short event, struct timeval *tv) event_debug_assert_is_setup_(ev); if (ev->ev_flags & EVLIST_INSERTED) - flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)); + flags |= (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)); if (ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)) flags |= ev->ev_res; if (ev->ev_flags & EVLIST_TIMEOUT) flags |= EV_TIMEOUT; - event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL); + event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL); /* See if there is a timeout that we should report */ if (tv != NULL && (flags & event & EV_TIMEOUT)) { @@ -2464,11 +2465,12 @@ event_add_nolock_(struct event *ev, const struct timeval *tv, event_debug_assert_is_setup_(ev); event_debug(( - "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%scall %p", + "event_add: event: %p (fd "EV_SOCK_FMT"), %s%s%s%scall %p", ev, EV_SOCK_ARG(ev->ev_fd), ev->ev_events & EV_READ ? "EV_READ " : " ", ev->ev_events & EV_WRITE ? "EV_WRITE " : " ", + ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ", tv ? "EV_TIMEOUT " : " ", ev->ev_callback)); @@ -2502,9 +2504,9 @@ event_add_nolock_(struct event *ev, const struct timeval *tv, } #endif - if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) && + if ((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED|EV_SIGNAL)) && !(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) { - if (ev->ev_events & (EV_READ|EV_WRITE)) + if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_add_(base, ev->ev_fd, ev); else if (ev->ev_events & EV_SIGNAL) res = evmap_signal_add_(base, (int)ev->ev_fd, ev); @@ -2731,7 +2733,7 @@ event_del_nolock_(struct event *ev, int blocking) if (ev->ev_flags & EVLIST_INSERTED) { event_queue_remove_inserted(base, ev); - if (ev->ev_events & (EV_READ|EV_WRITE)) + if (ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED)) res = evmap_io_del_(base, ev->ev_fd, ev); else res = evmap_signal_del_(base, (int)ev->ev_fd, ev); @@ -3602,10 +3604,11 @@ dump_inserted_event_fn(const struct event_base *base, const struct event *e, voi if (! (e->ev_flags & (EVLIST_INSERTED|EVLIST_TIMEOUT))) return 0; - fprintf(output, " %p [%s "EV_SOCK_FMT"]%s%s%s%s%s", + fprintf(output, " %p [%s "EV_SOCK_FMT"]%s%s%s%s%s%s", (void*)e, gloss, EV_SOCK_ARG(e->ev_fd), (e->ev_events&EV_READ)?" Read":"", (e->ev_events&EV_WRITE)?" Write":"", + (e->ev_events&EV_CLOSED)?" EOF":"", (e->ev_events&EV_SIGNAL)?" Signal":"", (e->ev_events&EV_PERSIST)?" Persist":"", (e->ev_flags&EVLIST_INTERNAL)?" Internal":""); @@ -3634,10 +3637,11 @@ dump_active_event_fn(const struct event_base *base, const struct event *e, void if (! (e->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))) return 0; - fprintf(output, " %p [%s "EV_SOCK_FMT", priority=%d]%s%s%s%s active%s%s\n", + fprintf(output, " %p [%s "EV_SOCK_FMT", priority=%d]%s%s%s%s%s active%s%s\n", (void*)e, gloss, EV_SOCK_ARG(e->ev_fd), e->ev_pri, (e->ev_res&EV_READ)?" Read":"", (e->ev_res&EV_WRITE)?" Write":"", + (e->ev_res&EV_CLOSED)?" EOF":"", (e->ev_res&EV_SIGNAL)?" Signal":"", (e->ev_res&EV_TIMEOUT)?" Timeout":"", (e->ev_flags&EVLIST_INTERNAL)?" [Internal]":"", @@ -3677,7 +3681,7 @@ void event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events) { EVBASE_ACQUIRE_LOCK(base, th_base_lock); - evmap_io_active_(base, fd, events & (EV_READ|EV_WRITE)); + evmap_io_active_(base, fd, events & (EV_READ|EV_WRITE|EV_CLOSED)); EVBASE_RELEASE_LOCK(base, th_base_lock); } diff --git a/evmap.c b/evmap.c index 26044b41c6..3f76dd0ae1 100644 --- a/evmap.c +++ b/evmap.c @@ -59,6 +59,7 @@ struct evmap_io { struct event_dlist events; ev_uint16_t nread; ev_uint16_t nwrite; + ev_uint16_t nclose; }; /* An entry for an evmap_signal list: notes all the events that want to know @@ -255,6 +256,7 @@ evmap_io_init(struct evmap_io *entry) LIST_INIT(&entry->events); entry->nread = 0; entry->nwrite = 0; + entry->nclose = 0; } @@ -266,7 +268,7 @@ evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev) const struct eventop *evsel = base->evsel; struct event_io_map *io = &base->io; struct evmap_io *ctx = NULL; - int nread, nwrite, retval = 0; + int nread, nwrite, nclose, retval = 0; short res = 0, old = 0; struct event *old_ev; @@ -286,11 +288,14 @@ evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev) nread = ctx->nread; nwrite = ctx->nwrite; + nclose = ctx->nclose; if (nread) old |= EV_READ; if (nwrite) old |= EV_WRITE; + if (nclose) + old |= EV_CLOSED; if (ev->ev_events & EV_READ) { if (++nread == 1) @@ -300,7 +305,11 @@ evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev) if (++nwrite == 1) res |= EV_WRITE; } - if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) { + if (ev->ev_events & EV_CLOSED) { + if (++nclose == 1) + res |= EV_CLOSED; + } + if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff || nclose > 0xffff)) { event_warnx("Too many events reading or writing on fd %d", (int)fd); return -1; @@ -326,6 +335,7 @@ evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev) ctx->nread = (ev_uint16_t) nread; ctx->nwrite = (ev_uint16_t) nwrite; + ctx->nclose = (ev_uint16_t) nclose; LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next); return (retval); @@ -339,7 +349,7 @@ evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev) const struct eventop *evsel = base->evsel; struct event_io_map *io = &base->io; struct evmap_io *ctx; - int nread, nwrite, retval = 0; + int nread, nwrite, nclose, retval = 0; short res = 0, old = 0; if (fd < 0) @@ -356,11 +366,14 @@ evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev) nread = ctx->nread; nwrite = ctx->nwrite; + nclose = ctx->nclose; if (nread) old |= EV_READ; if (nwrite) old |= EV_WRITE; + if (nclose) + old |= EV_CLOSED; if (ev->ev_events & EV_READ) { if (--nread == 0) @@ -372,6 +385,11 @@ evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev) res |= EV_WRITE; EVUTIL_ASSERT(nwrite >= 0); } + if (ev->ev_events & EV_CLOSED) { + if (--nclose == 0) + res |= EV_CLOSED; + EVUTIL_ASSERT(nclose >= 0); + } if (res) { void *extra = ((char*)ctx) + sizeof(struct evmap_io); @@ -384,6 +402,7 @@ evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev) ctx->nread = nread; ctx->nwrite = nwrite; + ctx->nclose = nclose; LIST_REMOVE(ev, ev_io_next); return (retval); @@ -589,6 +608,8 @@ evmap_io_reinit_iter_fn(struct event_base *base, evutil_socket_t fd, events |= EV_READ; if (ctx->nwrite) events |= EV_WRITE; + if (ctx->nclose) + events |= EV_CLOSED; if (evsel->fdinfo_len) memset(extra, 0, evsel->fdinfo_len); if (events && @@ -856,6 +877,10 @@ event_changelist_add_(struct event_base *base, evutil_socket_t fd, short old, sh change->write_change = EV_CHANGE_ADD | (events & (EV_ET|EV_PERSIST|EV_SIGNAL)); } + if (events & EV_CLOSED) { + change->close_change = EV_CHANGE_ADD | + (events & (EV_ET|EV_PERSIST|EV_SIGNAL)); + } event_changelist_check(base); return (0); @@ -902,6 +927,12 @@ event_changelist_del_(struct event_base *base, evutil_socket_t fd, short old, sh else change->write_change = EV_CHANGE_DEL; } + if (events & EV_CLOSED) { + if (!(change->old_events & EV_CLOSED)) + change->close_change = 0; + else + change->close_change = EV_CHANGE_DEL; + } event_changelist_check(base); return (0); @@ -915,7 +946,7 @@ evmap_io_check_integrity_fn(struct event_base *base, evutil_socket_t fd, struct evmap_io *io_info, void *arg) { struct event *ev; - int n_read = 0, n_write = 0; + int n_read = 0, n_write = 0, n_close = 0; /* First, make sure the list itself isn't corrupt. Otherwise, * running LIST_FOREACH could be an exciting adventure. */ @@ -925,15 +956,18 @@ evmap_io_check_integrity_fn(struct event_base *base, evutil_socket_t fd, EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED); EVUTIL_ASSERT(ev->ev_fd == fd); EVUTIL_ASSERT(!(ev->ev_events & EV_SIGNAL)); - EVUTIL_ASSERT((ev->ev_events & (EV_READ|EV_WRITE))); + EVUTIL_ASSERT((ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))); if (ev->ev_events & EV_READ) ++n_read; if (ev->ev_events & EV_WRITE) ++n_write; + if (ev->ev_events & EV_CLOSED) + ++n_close; } EVUTIL_ASSERT(n_read == io_info->nread); EVUTIL_ASSERT(n_write == io_info->nwrite); + EVUTIL_ASSERT(n_close == io_info->nclose); return 0; } @@ -952,7 +986,7 @@ evmap_signal_check_integrity_fn(struct event_base *base, EVUTIL_ASSERT(ev->ev_flags & EVLIST_INSERTED); EVUTIL_ASSERT(ev->ev_fd == signum); EVUTIL_ASSERT((ev->ev_events & EV_SIGNAL)); - EVUTIL_ASSERT(!(ev->ev_events & (EV_READ|EV_WRITE))); + EVUTIL_ASSERT(!(ev->ev_events & (EV_READ|EV_WRITE|EV_CLOSED))); } return 0; } diff --git a/include/event2/event.h b/include/event2/event.h index 0c94e14671..7632c0ee7c 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -488,7 +488,10 @@ enum event_method_feature { EV_FEATURE_O1 = 0x02, /** Require an event method that allows file descriptors as well as * sockets. */ - EV_FEATURE_FDS = 0x04 + EV_FEATURE_FDS = 0x04, + /** Require an event method that detects premature connection close by + * clients without the necessity of reading all the pending data. */ + EV_FEATURE_EARLY_CLOSE = 0x08 }; /** @@ -904,6 +907,11 @@ int event_base_got_break(struct event_base *); * BECOMES STABLE. **/ #define EV_FINALIZE 0x40 +/** + * Detects premature connection close by clients without the necessity of + * reading all the pending data, if supported by the backend. + **/ +#define EV_CLOSED 0x80 /**@}*/ /** diff --git a/make_epoll_table.py b/make_epoll_table.py index 37d6df1edc..1b15a91a67 100755 --- a/make_epoll_table.py +++ b/make_epoll_table.py @@ -1,15 +1,17 @@ #!/usr/bin/python2 -def get(old,wc,rc): - if ('xxx' in (rc, wc)): - return "0",-1 +def get(old,wc,rc,cc): + if ('xxx' in (rc, wc, cc)): + return "0",255 - if ('add' in (rc, wc)): + if ('add' in (rc, wc, cc)): events = [] if rc == 'add' or (rc != 'del' and 'r' in old): events.append("EPOLLIN") if wc == 'add' or (wc != 'del' and 'w' in old): events.append("EPOLLOUT") + if cc == 'add' or (cc != 'del' and 'c' in old): + events.append("EPOLLRDHUP") if old == "0": op = "EPOLL_CTL_ADD" @@ -17,41 +19,45 @@ def get(old,wc,rc): op = "EPOLL_CTL_MOD" return "|".join(events), op - if ('del' in (rc, wc)): + if ('del' in (rc, wc, cc)): + delevents = [] + modevents = [] op = "EPOLL_CTL_DEL" - if rc == 'del': - if wc == 'del': - events = "EPOLLIN|EPOLLOUT" - elif 'w' in old: - events = "EPOLLOUT" - op = "EPOLL_CTL_MOD" - else: - events = "EPOLLIN" + + if 'r' in old: + modevents.append("EPOLLIN") + if 'w' in old: + modevents.append("EPOLLOUT") + if 'c' in old: + modevents.append("EPOLLRDHUP") + + for item, event in [(rc,"EPOLLIN"), + (wc,"EPOLLOUT"), + (cc,"EPOLLRDHUP")]: + if item == 'del': + delevents.append(event) + if event in modevents: + modevents.remove(event) + + if modevents: + return "|".join(modevents), "EPOLL_CTL_MOD" else: - assert wc == 'del' - if 'r' in old: - events = "EPOLLIN" - op = "EPOLL_CTL_MOD" - else: - events = "EPOLLOUT" - return events, op + return "|".join(delevents), "EPOLL_CTL_DEL" return 0, 0 -def fmt(op, ev, old, wc, rc): +def fmt(op, ev, old, wc, rc, cc): entry = "{ %s, %s },"%(op, ev) - assert len(entry)<=36 - sp = " "*(36-len(entry)) - print "\t%s%s/* old=%2s, write:%3s, read:%3s */" % ( - entry, sp, old, wc, rc) - + print "\t/* old=%3s, write:%3s, read:%3s, close:%3s */\n\t%s" % ( + old, wc, rc, cc, entry) + return len(entry) -for old in ('0','r','w','rw'): +for old in ('0','r','w','rw','c','cr','cw','crw'): for wc in ('0', 'add', 'del', 'xxx'): for rc in ('0', 'add', 'del', 'xxx'): + for cc in ('0', 'add', 'del', 'xxx'): - op,ev = get(old,wc,rc) - - fmt(op, ev, old, wc, rc) + op,ev = get(old,wc,rc,cc) + fmt(op, ev, old, wc, rc, cc) diff --git a/test/Makefile.nmake b/test/Makefile.nmake index 92244ad7e4..a4609ac223 100644 --- a/test/Makefile.nmake +++ b/test/Makefile.nmake @@ -22,13 +22,13 @@ REGRESS_OBJS=regress.obj regress_buffer.obj regress_http.obj regress_dns.obj \ regress_main.obj regress_minheap.obj regress_iocp.obj \ regress_thread.obj regress_finalize.obj $(SSL_OBJS) -OTHER_OBJS=test-init.obj test-eof.obj test-weof.obj test-time.obj \ +OTHER_OBJS=test-init.obj test-eof.obj test-closed.obj test-weof.obj test-time.obj \ bench.obj bench_cascade.obj bench_http.obj bench_httpclient.obj \ test-changelist.obj \ print-winsock-errors.obj PROGRAMS=regress.exe \ - test-init.exe test-eof.exe test-weof.exe test-time.exe \ + test-init.exe test-eof.exe test-closed.exe test-weof.exe test-time.exe \ test-changelist.exe \ print-winsock-errors.exe @@ -47,6 +47,8 @@ test-init.exe: test-init.obj $(CC) $(CFLAGS) $(LIBS) test-init.obj test-eof.exe: test-eof.obj $(CC) $(CFLAGS) $(LIBS) test-eof.obj +test-closed.exe: test-closed.obj + $(CC) $(CFLAGS) $(LIBS) test-closed.obj test-changelist.exe: test-changelist.obj $(CC) $(CFLAGS) $(LIBS) test-changelist.obj test-weof.exe: test-weof.obj diff --git a/test/include.am b/test/include.am index 930d842250..629e9d277b 100644 --- a/test/include.am +++ b/test/include.am @@ -22,6 +22,7 @@ TESTPROGRAMS = \ test/test-changelist \ test/test-dumpevents \ test/test-eof \ + test/test-closed \ test/test-fdleak \ test/test-init \ test/test-ratelim \ @@ -60,6 +61,8 @@ test_test_dumpevents_SOURCES = test/test-dumpevents.c test_test_dumpevents_LDADD = libevent_core.la test_test_eof_SOURCES = test/test-eof.c test_test_eof_LDADD = libevent_core.la +test_test_closed_SOURCES = test/test-closed.c +test_test_closed_LDADD = libevent_core.la test_test_changelist_SOURCES = test/test-changelist.c test_test_changelist_LDADD = libevent_core.la test_test_weof_SOURCES = test/test-weof.c diff --git a/test/test-closed.c b/test/test-closed.c new file mode 100644 index 0000000000..5b04f354ac --- /dev/null +++ b/test/test-closed.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002-2007 Niels Provos + * Copyright (c) 2007-2013 Niels Provos and Nick Mathewson + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "event2/event-config.h" + +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#ifdef EVENT__HAVE_SYS_TIME_H +#include +#endif +#ifdef EVENT__HAVE_SYS_SOCKET_H +#include +#endif +#include +#include +#include +#include +#include + +#include +#include + +#ifdef EVENT____func__ +#define __func__ EVENT____func__ +#endif + +struct timeval timeout = {3, 0}; + +static void +closed_cb(evutil_socket_t fd, short event, void *arg) +{ + if (EV_TIMEOUT & event) { + printf("%s: Timeout!\n", __func__); + exit(1); + } + + if (EV_CLOSED & event) { + printf("%s: detected socket close with success\n", __func__); + return; + } + + printf("%s: unable to detect socket close\n", __func__); + exit(1); +} + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +int +main(int argc, char **argv) +{ + struct event_base *base; + struct event_config *cfg; + struct event *ev; + const char *test = "test string"; + evutil_socket_t pair[2]; + + /* Initialize the library and check if the backend + supports EV_FEATURE_EARLY_CLOSE + */ + cfg = event_config_new(); + event_config_require_features(cfg, EV_FEATURE_EARLY_CLOSE); + base = event_base_new_with_config(cfg); + event_config_free(cfg); + if (!base) { + /* Backend doesn't support EV_FEATURE_EARLY_CLOSE */ + return 0; + } + + /* Create a pair of sockets */ + if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) + return (1); + + /* Send some data on socket 0 and immediately close it */ + if (send(pair[0], test, (int)strlen(test)+1, 0) < 0) + return (1); + shutdown(pair[0], SHUT_WR); + + /* Dispatch */ + ev = event_new(base, pair[1], EV_CLOSED | EV_TIMEOUT, closed_cb, event_self_cbarg()); + event_add(ev, &timeout); + event_base_dispatch(base); + + /* Finalize library */ + event_base_free(base); + return 0; +} + diff --git a/test/test.sh b/test/test.sh index d16fcde33b..2b083ac7f3 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,7 +1,7 @@ #!/bin/sh BACKENDS="EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32" -TESTS="test-eof test-weof test-time test-changelist test-fdleak" +TESTS="test-eof test-closed test-weof test-time test-changelist test-fdleak" FAILED=no TEST_OUTPUT_FILE=${TEST_OUTPUT_FILE:-/dev/null} REGRESS_ARGS=${REGRESS_ARGS:-}