From 1480b7e9c09401c8b7cfe48f218e1648c2648d7f Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Fri, 28 May 2010 15:22:33 +0200 Subject: [PATCH] Fix bug #133 - Kqueue backend crash (BSD/OSX) --- src/network/kqueue.c | 151 +++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 47 deletions(-) diff --git a/src/network/kqueue.c b/src/network/kqueue.c index b788e02..014702d 100644 --- a/src/network/kqueue.c +++ b/src/network/kqueue.c @@ -32,19 +32,29 @@ struct net_connection_kqueue NET_CON_STRUCT_COMMON struct kevent ev_r; struct kevent ev_w; + int change; }; struct net_backend_kqueue { int kqfd; struct net_connection_kqueue** conns; - struct kevent** changes; - size_t nchanges; + struct kevent* changes; + int* change_list; + size_t change_list_len; struct kevent events[KQUEUE_EVBUFFER]; struct net_backend_common* common; }; +#define CHANGE_ACTION_ADD 0x0001 +#define CHANGE_ACTION_MOD 0x0002 +#define CHANGE_ACTION_DEL 0x0004 +#define CHANGE_OP_WANT_READ 0x0100 +#define CHANGE_OP_WANT_WRITE 0x0200 + static void net_backend_set_handlers(struct net_backend_handler* handler); +static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions); +static size_t create_change_list(struct net_backend_kqueue* backend); const char* net_backend_name_kqueue() { @@ -56,12 +66,13 @@ int net_backend_poll_kqueue(struct net_backend* data, int ms) int res; struct timespec tspec = { 0, }; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; + size_t changes; tspec.tv_sec = (ms / 1000); tspec.tv_nsec = ((ms % 1000) * 1000000); - res = kevent(backend->kqfd, *backend->changes, backend->nchanges, backend->events, KQUEUE_EVBUFFER, &tspec); - backend->nchanges = 0; + changes = create_change_list(backend); + res = kevent(backend->kqfd, backend->changes, changes, backend->events, KQUEUE_EVBUFFER, &tspec); if (res == -1 && errno == EINTR) return 0; @@ -76,11 +87,13 @@ void net_backend_process_kqueue(struct net_backend* data, int res) for (n = 0; n < res; n++) { struct net_connection_kqueue* con = (struct net_connection_kqueue*) backend->events[n].udata; - int ev = -1; - if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ; - else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE; - if (con) + if (con && con->sd >= 0 && backend->conns[con->sd]) + { + int ev = 0; + if (backend->events[n].filter == EVFILT_READ) ev = NET_EVENT_READ; + else if (backend->events[n].filter == EVFILT_WRITE) ev = NET_EVENT_WRITE; net_con_callback((struct net_connection*) con, ev); + } } } @@ -102,58 +115,37 @@ void net_con_initialize_kqueue(struct net_backend* data, struct net_connection* void net_con_backend_add_kqueue(struct net_backend* data, struct net_connection* con_, int events) { - unsigned short flags_r = EV_ADD; - unsigned short flags_w = EV_ADD; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; + int operation; backend->conns[con->sd] = con; - if (events & NET_EVENT_READ) - flags_r |= EV_ENABLE; - else - flags_r |= EV_DISABLE; + operation = CHANGE_ACTION_ADD; - EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con); - backend->changes[backend->nchanges++] = &con->ev_r; + if (events & NET_EVENT_READ) + operation |= CHANGE_OP_WANT_READ; if (events & NET_EVENT_WRITE) - flags_w |= EV_ENABLE; - else - flags_w |= EV_DISABLE; + operation |= CHANGE_OP_WANT_WRITE; - EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con); - backend->changes[backend->nchanges++] = &con->ev_w; + add_change(backend, con, operation); } void net_con_backend_mod_kqueue(struct net_backend* data, struct net_connection* con_, int events) { - unsigned short flags_r = 0; - unsigned short flags_w = 0; struct net_backend_kqueue* backend = (struct net_backend_kqueue*) data; struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; - if (events & NET_EVENT_READ) - flags_r |= EV_ENABLE; - else - flags_r |= EV_DISABLE; + int operation = CHANGE_ACTION_ADD; - if (!(con->ev_r.flags & flags_r)) - { - EV_SET(&con->ev_r, con->sd, EVFILT_READ, flags_r, 0, 0, con); - backend->changes[backend->nchanges++] = &con->ev_r; - } + if (events & NET_EVENT_READ) + operation |= CHANGE_OP_WANT_READ; if (events & NET_EVENT_WRITE) - flags_r |= EV_ENABLE; - else - flags_r |= EV_DISABLE; + operation |= CHANGE_OP_WANT_WRITE; - if (!(con->ev_w.flags & flags_w)) - { - EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, flags_w, 0, 0, con); - backend->changes[backend->nchanges++] = &con->ev_w; - } + add_change(backend, con, operation); } void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection* con_) @@ -162,11 +154,8 @@ void net_con_backend_del_kqueue(struct net_backend* data, struct net_connection* struct net_connection_kqueue* con = (struct net_connection_kqueue*) con_; /* No need to remove it from the kqueue filter, the kqueue man page says - it is automatically removed when the descriptor is closed. */ - EV_SET(&con->ev_r, con->sd, EVFILT_READ, EV_DELETE, 0, 0, 0); - backend->changes[backend->nchanges++] = &con->ev_r; - EV_SET(&con->ev_w, con->sd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); - backend->changes[backend->nchanges++] = &con->ev_w; + it is automatically removed when the descriptor is closed... */ + add_change(backend, con, CHANGE_ACTION_DEL); // Unmap the socket descriptor. backend->conns[con->sd] = 0; @@ -178,6 +167,7 @@ void net_backend_shutdown_kqueue(struct net_backend* data) close(backend->kqfd); hub_free(backend->conns); hub_free(backend->changes); + hub_free(backend->change_list); hub_free(backend); } @@ -197,8 +187,8 @@ struct net_backend* net_backend_init_kqueue(struct net_backend_handler* handler, } backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max); - backend->conns = hub_malloc_zero(sizeof(struct net_connection_kqueue*) * common->max); - backend->changes = hub_malloc_zero(sizeof(struct kevent*) * common->max * 2); + backend->changes = hub_malloc_zero(sizeof(struct kevent) * common->max * 2); + backend->change_list = hub_malloc_zero(sizeof(int) * common->max); backend->common = common; net_backend_set_handlers(handler); @@ -218,4 +208,71 @@ static void net_backend_set_handlers(struct net_backend_handler* handler) handler->con_del = net_con_backend_del_kqueue; } +static void add_change(struct net_backend_kqueue* backend, struct net_connection_kqueue* con, int actions) +{ + if (actions && !con->change) + { + backend->change_list[backend->change_list_len++] = con->sd; + con->change = actions; + } +} + +static size_t create_change_list(struct net_backend_kqueue* backend) +{ + size_t n = 0; + size_t changes = 0; + int sd; + struct net_connection_kqueue* con; + unsigned short flags_r = 0; + unsigned short flags_w = 0; + + for (; n < backend->change_list_len; n++) + { + sd = backend->change_list[n]; + con = backend->conns[sd]; + if (con) + { + flags_r = 0; + flags_w = 0; + + if (con->change & CHANGE_ACTION_ADD) + { + flags_r |= EV_ADD; + flags_w |= EV_ADD; + } + + if (con->change & CHANGE_OP_WANT_READ) + flags_r |= EV_ENABLE; + else + flags_r |= EV_DISABLE; + + if (con->change & CHANGE_OP_WANT_WRITE) + flags_w |= EV_ENABLE; + else + flags_w |= EV_DISABLE; + + if (con->ev_r.flags != flags_r) + { + EV_SET(&con->ev_r, sd, EVFILT_READ, flags_r, 0, 0, con); + memcpy(&backend->changes[changes++], &con->ev_r, sizeof(struct kevent)); + } + + if (con->ev_w.flags != flags_w) + { + EV_SET(&con->ev_w, sd, EVFILT_WRITE, flags_w, 0, 0, con); + memcpy(&backend->changes[changes++], &con->ev_w, sizeof(struct kevent)); + } + + con->change = 0; + } + else + { + EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0); + EV_SET(&backend->changes[changes++], sd, EVFILT_READ, EV_DELETE, 0, 0, 0); + } + } + backend->change_list_len = 0; + return changes; +} + #endif /* USE_KQUEUE */