mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-11 05:14:46 -04:00
Refactor evconnlistener to allow multiple implementations; add an (incomplete, not-yet-integrated) IOCP implementation.
svn:r1480
This commit is contained in:
parent
fa313f28c5
commit
20f5bdfd6b
315
listener.c
315
listener.c
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <mswsock.h>
|
||||||
#endif
|
#endif
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#ifdef _EVENT_HAVE_SYS_SOCKET_H
|
#ifdef _EVENT_HAVE_SYS_SOCKET_H
|
||||||
@ -51,14 +53,51 @@
|
|||||||
#include "mm-internal.h"
|
#include "mm-internal.h"
|
||||||
#include "util-internal.h"
|
#include "util-internal.h"
|
||||||
#include "log-internal.h"
|
#include "log-internal.h"
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "iocp-internal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct evconnlistener_ops {
|
||||||
|
int (*enable)(struct evconnlistener *);
|
||||||
|
int (*disable)(struct evconnlistener *);
|
||||||
|
void (*destroy)(struct evconnlistener *);
|
||||||
|
evutil_socket_t (*getfd)(struct evconnlistener *);
|
||||||
|
};
|
||||||
|
|
||||||
struct evconnlistener {
|
struct evconnlistener {
|
||||||
struct event listener;
|
const struct evconnlistener_ops *ops;
|
||||||
evconnlistener_cb cb;
|
evconnlistener_cb cb;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct evconnlistener_event {
|
||||||
|
struct evconnlistener base;
|
||||||
|
struct event listener;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
struct evconnlistener_iocp {
|
||||||
|
struct evconnlistener base;
|
||||||
|
evutil_socket_t fd;
|
||||||
|
struct event_iocp_port *port;
|
||||||
|
int n_accepting;
|
||||||
|
struct accepting_socket **accepting;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int event_listener_enable(struct evconnlistener *);
|
||||||
|
static int event_listener_disable(struct evconnlistener *);
|
||||||
|
static void event_listener_destroy(struct evconnlistener *);
|
||||||
|
static evutil_socket_t event_listener_getfd(struct evconnlistener *);
|
||||||
|
|
||||||
|
static const struct evconnlistener_ops evconnlistener_event_ops = {
|
||||||
|
event_listener_enable,
|
||||||
|
event_listener_disable,
|
||||||
|
event_listener_destroy,
|
||||||
|
event_listener_getfd
|
||||||
|
};
|
||||||
|
|
||||||
static void listener_read_cb(evutil_socket_t, short, void *);
|
static void listener_read_cb(evutil_socket_t, short, void *);
|
||||||
|
|
||||||
struct evconnlistener *
|
struct evconnlistener *
|
||||||
@ -66,7 +105,7 @@ evconnlistener_new(struct event_base *base,
|
|||||||
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
|
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
|
||||||
evutil_socket_t fd)
|
evutil_socket_t fd)
|
||||||
{
|
{
|
||||||
struct evconnlistener *lev;
|
struct evconnlistener_event *lev;
|
||||||
if (backlog > 0) {
|
if (backlog > 0) {
|
||||||
if (listen(fd, backlog) < 0)
|
if (listen(fd, backlog) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -74,21 +113,23 @@ evconnlistener_new(struct event_base *base,
|
|||||||
if (listen(fd, 128) < 0)
|
if (listen(fd, 128) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
lev = mm_calloc(1, sizeof(struct evconnlistener));
|
lev = mm_calloc(1, sizeof(struct evconnlistener_event));
|
||||||
if (!lev)
|
if (!lev)
|
||||||
return NULL;
|
return NULL;
|
||||||
lev->cb = cb;
|
lev->base.ops = &evconnlistener_event_ops;
|
||||||
lev->user_data = ptr;
|
lev->base.cb = cb;
|
||||||
lev->flags = flags;
|
lev->base.user_data = ptr;
|
||||||
|
lev->base.flags = flags;
|
||||||
event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,
|
event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,
|
||||||
listener_read_cb, lev);
|
listener_read_cb, lev);
|
||||||
evconnlistener_enable(lev);
|
evconnlistener_enable(&lev->base);
|
||||||
return lev;
|
return &lev->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct evconnlistener *
|
struct evconnlistener *
|
||||||
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr,
|
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
|
||||||
unsigned flags, int backlog, const struct sockaddr *sa, int socklen)
|
void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
|
||||||
|
int socklen)
|
||||||
{
|
{
|
||||||
evutil_socket_t fd;
|
evutil_socket_t fd;
|
||||||
int on = 1;
|
int on = 1;
|
||||||
@ -129,34 +170,70 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr
|
|||||||
void
|
void
|
||||||
evconnlistener_free(struct evconnlistener *lev)
|
evconnlistener_free(struct evconnlistener *lev)
|
||||||
{
|
{
|
||||||
event_del(&lev->listener);
|
lev->ops->destroy(lev);
|
||||||
if (lev->flags & LEV_OPT_CLOSE_ON_FREE)
|
|
||||||
EVUTIL_CLOSESOCKET(event_get_fd(&lev->listener));
|
|
||||||
mm_free(lev);
|
mm_free(lev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
event_listener_destroy(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
struct evconnlistener_event *lev_e =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
|
||||||
|
|
||||||
|
event_del(&lev_e->listener);
|
||||||
|
if (lev->flags & LEV_OPT_CLOSE_ON_FREE)
|
||||||
|
EVUTIL_CLOSESOCKET(event_get_fd(&lev_e->listener));
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
evconnlistener_enable(struct evconnlistener *lev)
|
evconnlistener_enable(struct evconnlistener *lev)
|
||||||
{
|
{
|
||||||
return event_add(&lev->listener, NULL);
|
return lev->ops->enable(lev);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
evconnlistener_disable(struct evconnlistener *lev)
|
evconnlistener_disable(struct evconnlistener *lev)
|
||||||
{
|
{
|
||||||
return event_del(&lev->listener);
|
return lev->ops->disable(lev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
event_listener_enable(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
struct evconnlistener_event *lev_e =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
|
||||||
|
return event_add(&lev_e->listener, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
event_listener_disable(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
struct evconnlistener_event *lev_e =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
|
||||||
|
return event_del(&lev_e->listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct event_base *
|
struct event_base *
|
||||||
evconnlistener_get_base(struct evconnlistener *lev)
|
evconnlistener_get_base(struct evconnlistener *lev)
|
||||||
{
|
{
|
||||||
return event_get_base(&lev->listener);
|
/* XXXX UPCAST. */
|
||||||
|
struct evconnlistener_event *lev_e =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
|
||||||
|
return event_get_base(&lev_e->listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
evutil_socket_t
|
evutil_socket_t
|
||||||
evconnlistener_get_fd(struct evconnlistener *lev)
|
evconnlistener_get_fd(struct evconnlistener *lev)
|
||||||
{
|
{
|
||||||
return event_get_fd(&lev->listener);
|
return lev->ops->getfd(lev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static evutil_socket_t
|
||||||
|
event_listener_getfd(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
struct evconnlistener_event *lev_e =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_event, base);
|
||||||
|
return event_get_fd(&lev_e->listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -183,3 +260,207 @@ listener_read_cb(evutil_socket_t fd, short what, void *p)
|
|||||||
return;
|
return;
|
||||||
event_sock_warn(fd, "Error from accept() call");
|
event_sock_warn(fd, "Error from accept() call");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
struct accepting_socket {
|
||||||
|
struct event_overlapped overlapped;
|
||||||
|
SOCKET s;
|
||||||
|
struct evconnlistener_iocp *lev;
|
||||||
|
int buflen;
|
||||||
|
char addrbuf[1]; /*XXX */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void accepted_socket_cb(struct event_overlapped *o, uintptr_t key,
|
||||||
|
ev_ssize_t n);
|
||||||
|
|
||||||
|
static struct accepting_socket *
|
||||||
|
new_accepting_socket(struct evconnlistener_iocp *lev, int family)
|
||||||
|
{
|
||||||
|
struct accepting_socket *res;
|
||||||
|
int addrlen;
|
||||||
|
int buflen;
|
||||||
|
if (family == AF_INET)
|
||||||
|
addrlen = sizeof(struct sockaddr_in);
|
||||||
|
else if (family == AF_INET6)
|
||||||
|
addrlen = sizeof(struct sockaddr_in6);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
buflen = (addrlen+16)*2;
|
||||||
|
|
||||||
|
res = mm_calloc(1,sizeof(struct accepting_socket)-1+buflen);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
event_overlapped_init(&res->overlapped, accepted_socket_cb);
|
||||||
|
res->s = INVALID_SOCKET;
|
||||||
|
res->lev = lev;
|
||||||
|
res->buflen = buflen;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
start_accepting(struct accepting_socket *as)
|
||||||
|
{
|
||||||
|
const struct win32_extension_fns *ext =
|
||||||
|
event_get_win32_extension_fns();
|
||||||
|
int family = AF_INET; /* XXXX */
|
||||||
|
SOCKET s = socket(family, SOCK_STREAM, 0);
|
||||||
|
DWORD pending = 0;
|
||||||
|
if (s == INVALID_SOCKET)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
setsockopt(s,
|
||||||
|
SOL_SOCKET,
|
||||||
|
SO_UPDATE_ACCEPT_CONTEXT,
|
||||||
|
(char *)&as->lev->fd,
|
||||||
|
sizeof(&as->lev->fd));
|
||||||
|
|
||||||
|
if (!(as->lev->base.flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING))
|
||||||
|
evutil_make_socket_nonblocking(s);
|
||||||
|
|
||||||
|
if (event_iocp_port_associate(as->lev->port, s, 1) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
as->s = s;
|
||||||
|
|
||||||
|
if (ext->AcceptEx(as->lev->fd, s, as->addrbuf, 0,
|
||||||
|
as->buflen/2, as->buflen/2,
|
||||||
|
&pending, &as->overlapped.overlapped)) {
|
||||||
|
/* Immediate success! */
|
||||||
|
accepted_socket_cb(&as->overlapped, 1, 0);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int err = WSAGetLastError();
|
||||||
|
if (err == ERROR_IO_PENDING)
|
||||||
|
return 0;
|
||||||
|
/* XXXX log the error */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
accepted_socket_cb(struct event_overlapped *o, uintptr_t key, ev_ssize_t n)
|
||||||
|
{
|
||||||
|
/* Run this whole thing deferred unless some MT flag is set */
|
||||||
|
|
||||||
|
struct sockaddr *sa_local=NULL, *sa_remote=NULL;
|
||||||
|
int socklen_local=0, socklen_remote=0;
|
||||||
|
struct accepting_socket *as =
|
||||||
|
EVUTIL_UPCAST(o, struct accepting_socket, overlapped);
|
||||||
|
const struct win32_extension_fns *ext =
|
||||||
|
event_get_win32_extension_fns();
|
||||||
|
|
||||||
|
EVUTIL_ASSERT(ext->GetAcceptExSockaddrs);
|
||||||
|
|
||||||
|
ext->GetAcceptExSockaddrs(as->addrbuf, 0,
|
||||||
|
as->buflen/2, as->buflen/2,
|
||||||
|
&sa_local, &socklen_local,
|
||||||
|
&sa_remote, &socklen_remote);
|
||||||
|
|
||||||
|
as->s = INVALID_SOCKET;
|
||||||
|
|
||||||
|
as->lev->base.cb(&as->lev->base, as->s, sa_remote, socklen_remote,
|
||||||
|
as->lev->base.user_data);
|
||||||
|
|
||||||
|
/* Avoid stack overflow XXXX */
|
||||||
|
start_accepting(as);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
iocp_listener_enable(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
/* XXXX */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
iocp_listener_disable(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
/* XXXX */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
iocp_listener_destroy(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
/* XXXX */
|
||||||
|
}
|
||||||
|
static evutil_socket_t
|
||||||
|
iocp_listener_getfd(struct evconnlistener *lev)
|
||||||
|
{
|
||||||
|
struct evconnlistener_iocp *lev_iocp =
|
||||||
|
EVUTIL_UPCAST(lev, struct evconnlistener_iocp, base);
|
||||||
|
return lev_iocp->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct evconnlistener_ops evconnlistener_iocp_ops = {
|
||||||
|
iocp_listener_enable,
|
||||||
|
iocp_listener_disable,
|
||||||
|
iocp_listener_destroy,
|
||||||
|
iocp_listener_getfd
|
||||||
|
};
|
||||||
|
|
||||||
|
struct evconnlistener *
|
||||||
|
evconnlistener_new_async(struct event_base *base,
|
||||||
|
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
|
||||||
|
evutil_socket_t fd); /* XXXX Use or export this. */
|
||||||
|
|
||||||
|
struct evconnlistener *
|
||||||
|
evconnlistener_new_async(struct event_base *base,
|
||||||
|
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
|
||||||
|
evutil_socket_t fd)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage ss;
|
||||||
|
int socklen = sizeof(ss);
|
||||||
|
struct evconnlistener_iocp *lev;
|
||||||
|
/* XXXX duplicate code */
|
||||||
|
if (backlog > 0) {
|
||||||
|
if (listen(fd, backlog) < 0)
|
||||||
|
return NULL;
|
||||||
|
} else if (backlog < 0) {
|
||||||
|
if (listen(fd, 128) < 0)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (getsockname(fd, (struct sockaddr*)&ss, &socklen)) {
|
||||||
|
event_sock_warn(fd, "getsockname");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lev = mm_calloc(1, sizeof(struct evconnlistener_event));
|
||||||
|
if (!lev) {
|
||||||
|
event_warn("calloc");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lev->base.ops = &evconnlistener_iocp_ops;
|
||||||
|
lev->base.cb = cb;
|
||||||
|
lev->base.user_data = ptr;
|
||||||
|
lev->base.flags = flags;
|
||||||
|
|
||||||
|
lev->fd = fd;
|
||||||
|
|
||||||
|
lev->n_accepting = 1;
|
||||||
|
lev->accepting = mm_calloc(1, sizeof(struct accepting_socket *));
|
||||||
|
if (!lev->accepting) {
|
||||||
|
event_warn("calloc");
|
||||||
|
mm_free(lev);
|
||||||
|
closesocket(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lev->accepting[0] = new_accepting_socket(lev, ss.ss_family);
|
||||||
|
if (!lev->accepting[0]) {
|
||||||
|
event_warnx("Couldn't create accepting socket");
|
||||||
|
mm_free(lev->accepting);
|
||||||
|
mm_free(lev);
|
||||||
|
closesocket(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!start_accepting(lev->accepting[0])) {
|
||||||
|
event_warnx("Couldn't start accepting on socket");
|
||||||
|
/* XXX free everything */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &lev->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user