]> granicus.if.org Git - libevent/commitdiff
Implemented EV_CLOSED event for epoll backend (EPOLLRDHUP).
authorDiego Giagio <diego@giagio.com>
Sat, 18 Jan 2014 01:20:42 +0000 (23:20 -0200)
committerDiego Giagio <diego@giagio.com>
Sat, 18 Jan 2014 01:20:42 +0000 (23:20 -0200)
- 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
epoll.c
event.c
evmap.c
include/event2/event.h
make_epoll_table.py
test/Makefile.nmake
test/include.am
test/test-closed.c [new file with mode: 0644]
test/test.sh

index 0ec2eeafffaf7556fb8c4679ecc41efdb224685a..98fc52aebfa79483cb32af99bc1c4cd231c877b8 100644 (file)
@@ -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 2d07ff62317c6807ef00afcaad81628a525045a0..bd425ed5163b7bb4d3e5c98558264d0737b391b8 100644 (file)
--- a/epoll.c
+++ b/epoll.c
 #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 eea2c0fcde4d6167206846fcf0e367b2fb6c2960..c946cbf98a60a976175b5c3cb73e13d03ce5c4aa 100644 (file)
--- 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 26044b41c652ad03a9cb5a224ac9af9d235bbcb5..3f76dd0ae1f7024613be86fc91c8c6e3d7af087f 100644 (file)
--- 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;
 }
index 0c94e14671ff4079b1350b00aceb12fb31b05149..7632c0ee7ca91e05e375d5bbf45d109fbf539b7a 100644 (file)
@@ -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
 /**@}*/
 
 /**
index 37d6df1edc1ef73483cb7ddf5ea0c70e9eba5cd8..1b15a91a67b5269541f6bf8fa32eb85bc1e2f670 100755 (executable)
@@ -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)
index 92244ad7e4cc4a500fd6eef75e2ed22c68ad7358..a4609ac223b3afba4ddb8ce7f8c203d305ce0133 100644 (file)
@@ -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
index 930d842250650c771ba60e48518512376155023d..629e9d277bb3f36792d2403e17eaf76f8ebe1b9e 100644 (file)
@@ -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 (file)
index 0000000..5b04f35
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2002-2007 Niels Provos <provos@citi.umich.edu>
+ * 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 <winsock2.h>
+#else
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef EVENT__HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef EVENT__HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <event.h>
+#include <evutil.h>
+
+#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;
+}
+
index d16fcde33b012f5861f40929da5bc6ec8755556c..2b083ac7f3c9ce16d7bab7d0c1ade43015a6415a 100755 (executable)
@@ -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:-}