mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +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. */
|
* and write_change is unused. */
|
||||||
ev_uint8_t read_change;
|
ev_uint8_t read_change;
|
||||||
ev_uint8_t write_change;
|
ev_uint8_t write_change;
|
||||||
|
ev_uint8_t close_change;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Flags for read_change and write_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
|
else
|
||||||
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
|
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
|
||||||
event_debug((
|
event_debug((
|
||||||
"event_process_active: event: %p, %s%scall %p",
|
"event_process_active: event: %p, %s%s%scall %p",
|
||||||
ev,
|
ev,
|
||||||
ev->ev_res & EV_READ ? "EV_READ " : " ",
|
ev->ev_res & EV_READ ? "EV_READ " : " ",
|
||||||
ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",
|
ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",
|
||||||
|
ev->ev_res & EV_CLOSED ? "EV_CLOSED " : " ",
|
||||||
ev->ev_callback));
|
ev->ev_callback));
|
||||||
} else {
|
} else {
|
||||||
event_queue_remove_active(base, evcb);
|
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->cb = callback;
|
||||||
eonce->arg = arg;
|
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);
|
evtimer_assign(&eonce->ev, base, event_once_cb, eonce);
|
||||||
|
|
||||||
if (tv == NULL || ! evutil_timerisset(tv)) {
|
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). */
|
* it fast (and order-preserving). */
|
||||||
activate = 1;
|
activate = 1;
|
||||||
}
|
}
|
||||||
} else if (events & (EV_READ|EV_WRITE)) {
|
} else if (events & (EV_READ|EV_WRITE|EV_CLOSED)) {
|
||||||
events &= EV_READ|EV_WRITE;
|
events &= EV_READ|EV_WRITE|EV_CLOSED;
|
||||||
|
|
||||||
event_assign(&eonce->ev, base, fd, events, event_once_cb, eonce);
|
event_assign(&eonce->ev, base, fd, events, event_once_cb, eonce);
|
||||||
} else {
|
} else {
|
||||||
@ -1992,9 +1993,9 @@ event_assign(struct event *ev, struct event_base *base, evutil_socket_t fd, shor
|
|||||||
ev->ev_pncalls = NULL;
|
ev->ev_pncalls = NULL;
|
||||||
|
|
||||||
if (events & EV_SIGNAL) {
|
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 "
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
ev->ev_closure = EV_CLOSURE_EVENT_SIGNAL;
|
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);
|
event_debug_assert_is_setup_(ev);
|
||||||
|
|
||||||
if (ev->ev_flags & EVLIST_INSERTED)
|
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))
|
if (ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER))
|
||||||
flags |= ev->ev_res;
|
flags |= ev->ev_res;
|
||||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||||
flags |= EV_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 */
|
/* See if there is a timeout that we should report */
|
||||||
if (tv != NULL && (flags & event & EV_TIMEOUT)) {
|
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_assert_is_setup_(ev);
|
||||||
|
|
||||||
event_debug((
|
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,
|
||||||
EV_SOCK_ARG(ev->ev_fd),
|
EV_SOCK_ARG(ev->ev_fd),
|
||||||
ev->ev_events & EV_READ ? "EV_READ " : " ",
|
ev->ev_events & EV_READ ? "EV_READ " : " ",
|
||||||
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
|
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
|
||||||
|
ev->ev_events & EV_CLOSED ? "EV_CLOSED " : " ",
|
||||||
tv ? "EV_TIMEOUT " : " ",
|
tv ? "EV_TIMEOUT " : " ",
|
||||||
ev->ev_callback));
|
ev->ev_callback));
|
||||||
|
|
||||||
@ -2502,9 +2504,9 @@ event_add_nolock_(struct event *ev, const struct timeval *tv,
|
|||||||
}
|
}
|
||||||
#endif
|
#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))) {
|
!(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);
|
res = evmap_io_add_(base, ev->ev_fd, ev);
|
||||||
else if (ev->ev_events & EV_SIGNAL)
|
else if (ev->ev_events & EV_SIGNAL)
|
||||||
res = evmap_signal_add_(base, (int)ev->ev_fd, ev);
|
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) {
|
if (ev->ev_flags & EVLIST_INSERTED) {
|
||||||
event_queue_remove_inserted(base, ev);
|
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);
|
res = evmap_io_del_(base, ev->ev_fd, ev);
|
||||||
else
|
else
|
||||||
res = evmap_signal_del_(base, (int)ev->ev_fd, ev);
|
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)))
|
if (! (e->ev_flags & (EVLIST_INSERTED|EVLIST_TIMEOUT)))
|
||||||
return 0;
|
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),
|
(void*)e, gloss, EV_SOCK_ARG(e->ev_fd),
|
||||||
(e->ev_events&EV_READ)?" Read":"",
|
(e->ev_events&EV_READ)?" Read":"",
|
||||||
(e->ev_events&EV_WRITE)?" Write":"",
|
(e->ev_events&EV_WRITE)?" Write":"",
|
||||||
|
(e->ev_events&EV_CLOSED)?" EOF":"",
|
||||||
(e->ev_events&EV_SIGNAL)?" Signal":"",
|
(e->ev_events&EV_SIGNAL)?" Signal":"",
|
||||||
(e->ev_events&EV_PERSIST)?" Persist":"",
|
(e->ev_events&EV_PERSIST)?" Persist":"",
|
||||||
(e->ev_flags&EVLIST_INTERNAL)?" Internal":"");
|
(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)))
|
if (! (e->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER)))
|
||||||
return 0;
|
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,
|
(void*)e, gloss, EV_SOCK_ARG(e->ev_fd), e->ev_pri,
|
||||||
(e->ev_res&EV_READ)?" Read":"",
|
(e->ev_res&EV_READ)?" Read":"",
|
||||||
(e->ev_res&EV_WRITE)?" Write":"",
|
(e->ev_res&EV_WRITE)?" Write":"",
|
||||||
|
(e->ev_res&EV_CLOSED)?" EOF":"",
|
||||||
(e->ev_res&EV_SIGNAL)?" Signal":"",
|
(e->ev_res&EV_SIGNAL)?" Signal":"",
|
||||||
(e->ev_res&EV_TIMEOUT)?" Timeout":"",
|
(e->ev_res&EV_TIMEOUT)?" Timeout":"",
|
||||||
(e->ev_flags&EVLIST_INTERNAL)?" [Internal]":"",
|
(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)
|
event_base_active_by_fd(struct event_base *base, evutil_socket_t fd, short events)
|
||||||
{
|
{
|
||||||
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
|
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);
|
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
46
evmap.c
46
evmap.c
@ -59,6 +59,7 @@ struct evmap_io {
|
|||||||
struct event_dlist events;
|
struct event_dlist events;
|
||||||
ev_uint16_t nread;
|
ev_uint16_t nread;
|
||||||
ev_uint16_t nwrite;
|
ev_uint16_t nwrite;
|
||||||
|
ev_uint16_t nclose;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* An entry for an evmap_signal list: notes all the events that want to know
|
/* 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);
|
LIST_INIT(&entry->events);
|
||||||
entry->nread = 0;
|
entry->nread = 0;
|
||||||
entry->nwrite = 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;
|
const struct eventop *evsel = base->evsel;
|
||||||
struct event_io_map *io = &base->io;
|
struct event_io_map *io = &base->io;
|
||||||
struct evmap_io *ctx = NULL;
|
struct evmap_io *ctx = NULL;
|
||||||
int nread, nwrite, retval = 0;
|
int nread, nwrite, nclose, retval = 0;
|
||||||
short res = 0, old = 0;
|
short res = 0, old = 0;
|
||||||
struct event *old_ev;
|
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;
|
nread = ctx->nread;
|
||||||
nwrite = ctx->nwrite;
|
nwrite = ctx->nwrite;
|
||||||
|
nclose = ctx->nclose;
|
||||||
|
|
||||||
if (nread)
|
if (nread)
|
||||||
old |= EV_READ;
|
old |= EV_READ;
|
||||||
if (nwrite)
|
if (nwrite)
|
||||||
old |= EV_WRITE;
|
old |= EV_WRITE;
|
||||||
|
if (nclose)
|
||||||
|
old |= EV_CLOSED;
|
||||||
|
|
||||||
if (ev->ev_events & EV_READ) {
|
if (ev->ev_events & EV_READ) {
|
||||||
if (++nread == 1)
|
if (++nread == 1)
|
||||||
@ -300,7 +305,11 @@ evmap_io_add_(struct event_base *base, evutil_socket_t fd, struct event *ev)
|
|||||||
if (++nwrite == 1)
|
if (++nwrite == 1)
|
||||||
res |= EV_WRITE;
|
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",
|
event_warnx("Too many events reading or writing on fd %d",
|
||||||
(int)fd);
|
(int)fd);
|
||||||
return -1;
|
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->nread = (ev_uint16_t) nread;
|
||||||
ctx->nwrite = (ev_uint16_t) nwrite;
|
ctx->nwrite = (ev_uint16_t) nwrite;
|
||||||
|
ctx->nclose = (ev_uint16_t) nclose;
|
||||||
LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
|
LIST_INSERT_HEAD(&ctx->events, ev, ev_io_next);
|
||||||
|
|
||||||
return (retval);
|
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;
|
const struct eventop *evsel = base->evsel;
|
||||||
struct event_io_map *io = &base->io;
|
struct event_io_map *io = &base->io;
|
||||||
struct evmap_io *ctx;
|
struct evmap_io *ctx;
|
||||||
int nread, nwrite, retval = 0;
|
int nread, nwrite, nclose, retval = 0;
|
||||||
short res = 0, old = 0;
|
short res = 0, old = 0;
|
||||||
|
|
||||||
if (fd < 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;
|
nread = ctx->nread;
|
||||||
nwrite = ctx->nwrite;
|
nwrite = ctx->nwrite;
|
||||||
|
nclose = ctx->nclose;
|
||||||
|
|
||||||
if (nread)
|
if (nread)
|
||||||
old |= EV_READ;
|
old |= EV_READ;
|
||||||
if (nwrite)
|
if (nwrite)
|
||||||
old |= EV_WRITE;
|
old |= EV_WRITE;
|
||||||
|
if (nclose)
|
||||||
|
old |= EV_CLOSED;
|
||||||
|
|
||||||
if (ev->ev_events & EV_READ) {
|
if (ev->ev_events & EV_READ) {
|
||||||
if (--nread == 0)
|
if (--nread == 0)
|
||||||
@ -372,6 +385,11 @@ evmap_io_del_(struct event_base *base, evutil_socket_t fd, struct event *ev)
|
|||||||
res |= EV_WRITE;
|
res |= EV_WRITE;
|
||||||
EVUTIL_ASSERT(nwrite >= 0);
|
EVUTIL_ASSERT(nwrite >= 0);
|
||||||
}
|
}
|
||||||
|
if (ev->ev_events & EV_CLOSED) {
|
||||||
|
if (--nclose == 0)
|
||||||
|
res |= EV_CLOSED;
|
||||||
|
EVUTIL_ASSERT(nclose >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
void *extra = ((char*)ctx) + sizeof(struct evmap_io);
|
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->nread = nread;
|
||||||
ctx->nwrite = nwrite;
|
ctx->nwrite = nwrite;
|
||||||
|
ctx->nclose = nclose;
|
||||||
LIST_REMOVE(ev, ev_io_next);
|
LIST_REMOVE(ev, ev_io_next);
|
||||||
|
|
||||||
return (retval);
|
return (retval);
|
||||||
@ -589,6 +608,8 @@ evmap_io_reinit_iter_fn(struct event_base *base, evutil_socket_t fd,
|
|||||||
events |= EV_READ;
|
events |= EV_READ;
|
||||||
if (ctx->nwrite)
|
if (ctx->nwrite)
|
||||||
events |= EV_WRITE;
|
events |= EV_WRITE;
|
||||||
|
if (ctx->nclose)
|
||||||
|
events |= EV_CLOSED;
|
||||||
if (evsel->fdinfo_len)
|
if (evsel->fdinfo_len)
|
||||||
memset(extra, 0, evsel->fdinfo_len);
|
memset(extra, 0, evsel->fdinfo_len);
|
||||||
if (events &&
|
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 |
|
change->write_change = EV_CHANGE_ADD |
|
||||||
(events & (EV_ET|EV_PERSIST|EV_SIGNAL));
|
(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);
|
event_changelist_check(base);
|
||||||
return (0);
|
return (0);
|
||||||
@ -902,6 +927,12 @@ event_changelist_del_(struct event_base *base, evutil_socket_t fd, short old, sh
|
|||||||
else
|
else
|
||||||
change->write_change = EV_CHANGE_DEL;
|
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);
|
event_changelist_check(base);
|
||||||
return (0);
|
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 evmap_io *io_info, void *arg)
|
||||||
{
|
{
|
||||||
struct event *ev;
|
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,
|
/* First, make sure the list itself isn't corrupt. Otherwise,
|
||||||
* running LIST_FOREACH could be an exciting adventure. */
|
* 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_flags & EVLIST_INSERTED);
|
||||||
EVUTIL_ASSERT(ev->ev_fd == fd);
|
EVUTIL_ASSERT(ev->ev_fd == fd);
|
||||||
EVUTIL_ASSERT(!(ev->ev_events & EV_SIGNAL));
|
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)
|
if (ev->ev_events & EV_READ)
|
||||||
++n_read;
|
++n_read;
|
||||||
if (ev->ev_events & EV_WRITE)
|
if (ev->ev_events & EV_WRITE)
|
||||||
++n_write;
|
++n_write;
|
||||||
|
if (ev->ev_events & EV_CLOSED)
|
||||||
|
++n_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
EVUTIL_ASSERT(n_read == io_info->nread);
|
EVUTIL_ASSERT(n_read == io_info->nread);
|
||||||
EVUTIL_ASSERT(n_write == io_info->nwrite);
|
EVUTIL_ASSERT(n_write == io_info->nwrite);
|
||||||
|
EVUTIL_ASSERT(n_close == io_info->nclose);
|
||||||
|
|
||||||
return 0;
|
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_flags & EVLIST_INSERTED);
|
||||||
EVUTIL_ASSERT(ev->ev_fd == signum);
|
EVUTIL_ASSERT(ev->ev_fd == signum);
|
||||||
EVUTIL_ASSERT((ev->ev_events & EV_SIGNAL));
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -488,7 +488,10 @@ enum event_method_feature {
|
|||||||
EV_FEATURE_O1 = 0x02,
|
EV_FEATURE_O1 = 0x02,
|
||||||
/** Require an event method that allows file descriptors as well as
|
/** Require an event method that allows file descriptors as well as
|
||||||
* sockets. */
|
* 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.
|
* BECOMES STABLE.
|
||||||
**/
|
**/
|
||||||
#define EV_FINALIZE 0x40
|
#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
|
#!/usr/bin/python2
|
||||||
|
|
||||||
def get(old,wc,rc):
|
def get(old,wc,rc,cc):
|
||||||
if ('xxx' in (rc, wc)):
|
if ('xxx' in (rc, wc, cc)):
|
||||||
return "0",-1
|
return "0",255
|
||||||
|
|
||||||
if ('add' in (rc, wc)):
|
if ('add' in (rc, wc, cc)):
|
||||||
events = []
|
events = []
|
||||||
if rc == 'add' or (rc != 'del' and 'r' in old):
|
if rc == 'add' or (rc != 'del' and 'r' in old):
|
||||||
events.append("EPOLLIN")
|
events.append("EPOLLIN")
|
||||||
if wc == 'add' or (wc != 'del' and 'w' in old):
|
if wc == 'add' or (wc != 'del' and 'w' in old):
|
||||||
events.append("EPOLLOUT")
|
events.append("EPOLLOUT")
|
||||||
|
if cc == 'add' or (cc != 'del' and 'c' in old):
|
||||||
|
events.append("EPOLLRDHUP")
|
||||||
|
|
||||||
if old == "0":
|
if old == "0":
|
||||||
op = "EPOLL_CTL_ADD"
|
op = "EPOLL_CTL_ADD"
|
||||||
@ -17,41 +19,45 @@ def get(old,wc,rc):
|
|||||||
op = "EPOLL_CTL_MOD"
|
op = "EPOLL_CTL_MOD"
|
||||||
return "|".join(events), op
|
return "|".join(events), op
|
||||||
|
|
||||||
if ('del' in (rc, wc)):
|
if ('del' in (rc, wc, cc)):
|
||||||
|
delevents = []
|
||||||
|
modevents = []
|
||||||
op = "EPOLL_CTL_DEL"
|
op = "EPOLL_CTL_DEL"
|
||||||
if rc == 'del':
|
|
||||||
if wc == 'del':
|
if 'r' in old:
|
||||||
events = "EPOLLIN|EPOLLOUT"
|
modevents.append("EPOLLIN")
|
||||||
elif 'w' in old:
|
if 'w' in old:
|
||||||
events = "EPOLLOUT"
|
modevents.append("EPOLLOUT")
|
||||||
op = "EPOLL_CTL_MOD"
|
if 'c' in old:
|
||||||
else:
|
modevents.append("EPOLLRDHUP")
|
||||||
events = "EPOLLIN"
|
|
||||||
|
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:
|
else:
|
||||||
assert wc == 'del'
|
return "|".join(delevents), "EPOLL_CTL_DEL"
|
||||||
if 'r' in old:
|
|
||||||
events = "EPOLLIN"
|
|
||||||
op = "EPOLL_CTL_MOD"
|
|
||||||
else:
|
|
||||||
events = "EPOLLOUT"
|
|
||||||
return events, op
|
|
||||||
|
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
def fmt(op, ev, old, wc, rc):
|
def fmt(op, ev, old, wc, rc, cc):
|
||||||
entry = "{ %s, %s },"%(op, ev)
|
entry = "{ %s, %s },"%(op, ev)
|
||||||
assert len(entry)<=36
|
print "\t/* old=%3s, write:%3s, read:%3s, close:%3s */\n\t%s" % (
|
||||||
sp = " "*(36-len(entry))
|
old, wc, rc, cc, entry)
|
||||||
print "\t%s%s/* old=%2s, write:%3s, read:%3s */" % (
|
return len(entry)
|
||||||
entry, sp, old, wc, rc)
|
|
||||||
|
|
||||||
|
for old in ('0','r','w','rw','c','cr','cw','crw'):
|
||||||
for old in ('0','r','w','rw'):
|
|
||||||
for wc in ('0', 'add', 'del', 'xxx'):
|
for wc in ('0', 'add', 'del', 'xxx'):
|
||||||
for rc 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)
|
op,ev = get(old,wc,rc,cc)
|
||||||
|
|
||||||
fmt(op, ev, old, wc, rc)
|
|
||||||
|
|
||||||
|
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_main.obj regress_minheap.obj regress_iocp.obj \
|
||||||
regress_thread.obj regress_finalize.obj $(SSL_OBJS)
|
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 \
|
bench.obj bench_cascade.obj bench_http.obj bench_httpclient.obj \
|
||||||
test-changelist.obj \
|
test-changelist.obj \
|
||||||
print-winsock-errors.obj
|
print-winsock-errors.obj
|
||||||
|
|
||||||
PROGRAMS=regress.exe \
|
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 \
|
test-changelist.exe \
|
||||||
print-winsock-errors.exe
|
print-winsock-errors.exe
|
||||||
|
|
||||||
@ -47,6 +47,8 @@ test-init.exe: test-init.obj
|
|||||||
$(CC) $(CFLAGS) $(LIBS) test-init.obj
|
$(CC) $(CFLAGS) $(LIBS) test-init.obj
|
||||||
test-eof.exe: test-eof.obj
|
test-eof.exe: test-eof.obj
|
||||||
$(CC) $(CFLAGS) $(LIBS) 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
|
test-changelist.exe: test-changelist.obj
|
||||||
$(CC) $(CFLAGS) $(LIBS) test-changelist.obj
|
$(CC) $(CFLAGS) $(LIBS) test-changelist.obj
|
||||||
test-weof.exe: test-weof.obj
|
test-weof.exe: test-weof.obj
|
||||||
|
@ -22,6 +22,7 @@ TESTPROGRAMS = \
|
|||||||
test/test-changelist \
|
test/test-changelist \
|
||||||
test/test-dumpevents \
|
test/test-dumpevents \
|
||||||
test/test-eof \
|
test/test-eof \
|
||||||
|
test/test-closed \
|
||||||
test/test-fdleak \
|
test/test-fdleak \
|
||||||
test/test-init \
|
test/test-init \
|
||||||
test/test-ratelim \
|
test/test-ratelim \
|
||||||
@ -60,6 +61,8 @@ test_test_dumpevents_SOURCES = test/test-dumpevents.c
|
|||||||
test_test_dumpevents_LDADD = libevent_core.la
|
test_test_dumpevents_LDADD = libevent_core.la
|
||||||
test_test_eof_SOURCES = test/test-eof.c
|
test_test_eof_SOURCES = test/test-eof.c
|
||||||
test_test_eof_LDADD = libevent_core.la
|
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_SOURCES = test/test-changelist.c
|
||||||
test_test_changelist_LDADD = libevent_core.la
|
test_test_changelist_LDADD = libevent_core.la
|
||||||
test_test_weof_SOURCES = test/test-weof.c
|
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
|
#!/bin/sh
|
||||||
|
|
||||||
BACKENDS="EVPORT KQUEUE EPOLL DEVPOLL POLL SELECT WIN32"
|
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
|
FAILED=no
|
||||||
TEST_OUTPUT_FILE=${TEST_OUTPUT_FILE:-/dev/null}
|
TEST_OUTPUT_FILE=${TEST_OUTPUT_FILE:-/dev/null}
|
||||||
REGRESS_ARGS=${REGRESS_ARGS:-}
|
REGRESS_ARGS=${REGRESS_ARGS:-}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user