mirror of
https://github.com/libevent/libevent.git
synced 2025-01-09 00:56:20 +08:00
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
This commit is contained in:
parent
d240328d60
commit
b1b69ac7c1
@ -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. */
|
||||
|
34
event.c
34
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);
|
||||
}
|
||||
|
||||
|
46
evmap.c
46
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;
|
||||
}
|
||||
|
@ -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
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
117
test/test-closed.c
Normal file
117
test/test-closed.c
Normal file
@ -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;
|
||||
}
|
||||
|
@ -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:-}
|
||||
|
Loading…
x
Reference in New Issue
Block a user