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:
Diego Giagio 2014-01-17 23:20:42 -02:00
parent d240328d60
commit b1b69ac7c1
10 changed files with 1297 additions and 137 deletions

View 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. */

1149
epoll.c

File diff suppressed because it is too large Load Diff

34
event.c
View File

@ -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
View File

@ -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;
}

View 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
/**@}*/
/**

View File

@ -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)

View 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

View 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

117
test/test-closed.c Normal file
View 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;
}

View File

@ -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:-}