#include /* closing sockets - wait for proper shutdown */ typedef struct closing_socket closing_socket; struct closing_socket { liEventLoop *loop; GList sockets_link; int fd; li_tstamp close_timeout; }; static void close_socket_now(closing_socket *cs) { close(cs->fd); cs->fd = -1; g_queue_unlink(&cs->loop->closing_sockets, &cs->sockets_link); } static void closing_socket_cb(int revents, void* arg) { static char trash[1024]; closing_socket *cs = (closing_socket*) arg; ssize_t r; liEventLoop *loop = cs->loop; li_tstamp remaining = cs->close_timeout - li_event_now(loop); if (-1 == cs->fd) { g_slice_free(closing_socket, cs); return; } /* empty the input buffer, wait for EOF or timeout or a socket error to close it */ for (;!loop->end;) { r = read(cs->fd, trash, sizeof(trash)); if (0 == r) break; /* got EOF */ if (0 > r) { /* error */ switch (errno) { case EINTR: /* call read again */ continue; case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif /* check timeout: */ if (remaining > 0 && !(revents & EV_TIMEOUT)) { /* wait again */ ev_once(cs->loop->loop, cs->fd, EV_READ, remaining, closing_socket_cb, cs); return; } /* timeout reached, break switch and loop */ break; default: /* real error (probably ECONNRESET or similar): break switch and loop */ /* no logging: there is no context anymore for the socket */ break; } break; /* end loop */ } } close_socket_now(cs); g_slice_free(closing_socket, cs); } void li_event_add_closing_socket(liEventLoop *loop, int fd) { closing_socket *cs; if (-1 == fd) return; shutdown(fd, SHUT_WR); if (loop->end) { close(fd); return; } cs = g_slice_new0(closing_socket); cs->loop = loop; cs->fd = fd; g_queue_push_tail_link(&loop->closing_sockets, &cs->sockets_link); cs->close_timeout = li_event_now(loop) + 10.0; ev_once(loop->loop, fd, EV_READ, 10.0, closing_socket_cb, cs); } void li_event_loop_init(liEventLoop *loop, struct ev_loop *evloop) { ev_ref(evloop); loop->end = 0; loop->loop = evloop; g_queue_init(&loop->watchers); g_queue_init(&loop->closing_sockets); li_job_queue_init(&loop->jobqueue, loop); } struct ev_loop* li_event_loop_clear(liEventLoop *loop) { struct ev_loop* evloop = loop->loop; GList *lnk; li_event_loop_end(loop); li_job_queue_clear(&loop->jobqueue); while (NULL != (lnk = loop->watchers.head)) { liEventBase *base = LI_CONTAINER_OF(lnk, liEventBase, link_watchers); assert(li_event_attached_(base)); li_event_detach_(base); assert(lnk != loop->watchers.head); } loop->loop = NULL; return evloop; } void li_event_loop_run(liEventLoop *loop) { ev_loop(loop->loop, 0); } void li_event_loop_end(liEventLoop *loop) { if (loop->end) return; loop->end = TRUE; ev_unref(loop->loop); li_event_loop_force_close_sockets(loop); } void li_event_loop_exit(liEventLoop *loop) { li_event_loop_end(loop); ev_unloop(loop->loop, EVUNLOOP_ALL); } void li_event_loop_force_close_sockets(liEventLoop *loop) { GList *lnk; while (NULL != (lnk = loop->closing_sockets.head)) { closing_socket *cs = LI_CONTAINER_OF(lnk, closing_socket, sockets_link); ev_feed_fd_event(loop->loop, cs->fd, EV_READ); close_socket_now(cs); } } const char* li_event_loop_backend_string(liEventLoop *loop) { switch (ev_backend(loop->loop)) { case EVBACKEND_SELECT: return "select"; case EVBACKEND_POLL: return "poll"; case EVBACKEND_EPOLL: return "epoll"; case EVBACKEND_KQUEUE: return "kqueue"; case EVBACKEND_DEVPOLL: return "devpoll"; case EVBACKEND_PORT: return "port"; default: return "unknown"; } } static void event_io_cb(struct ev_loop *loop, ev_io *w, int revents) { liEventIO *io = LI_CONTAINER_OF(w, liEventIO, libevmess.io); liEventLoop *my_loop = io->base.link_watchers.data; int events = 0; assert(NULL != my_loop); assert(loop == my_loop->loop); if (revents & EV_READ) events |= LI_EV_READ; if (revents & EV_WRITE) events |= LI_EV_WRITE; io->base.callback(&io->base, events); } static int io_events_to_libev(int events) { int revents = 0; if (events & LI_EV_READ) revents |= EV_READ; if (events & LI_EV_WRITE) revents |= EV_WRITE; return revents; } void li_event_io_init(liEventLoop *loop, liEventIO *io, liEventCallback callback, int fd, int events) { memset(io, 0, sizeof(*io)); io->base.type = LI_EVT_IO; io->base.keep_loop_alive = 1; io->base.callback = callback; io->events = events; ev_init(&io->libevmess.w, NULL); ev_io_set(&io->libevmess.io, fd, io_events_to_libev(events)); ev_set_cb(&io->libevmess.io, event_io_cb); if (NULL != loop) li_event_attach(loop, io); } void li_event_io_set_fd(liEventIO *io, int fd) { if (-1 == fd) { li_event_stop(io); ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); return; } if (li_event_attached(io) && li_event_active(io)) { liEventLoop *loop = io->base.link_watchers.data; assert(NULL != loop); ev_ref(loop->loop); ev_io_stop(loop->loop, &io->libevmess.io); ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); ev_io_start(loop->loop, &io->libevmess.io); ev_unref(loop->loop); } else { ev_io_set(&io->libevmess.io, fd, io->libevmess.io.events); } } void li_event_io_set_events(liEventIO *io, int events) { if (events == io->events) return; io->events = events; if (li_event_attached(io) && li_event_active(io)) { liEventLoop *loop = io->base.link_watchers.data; assert(NULL != loop); ev_ref(loop->loop); ev_io_stop(loop->loop, &io->libevmess.io); ev_io_set(&io->libevmess.io, io->libevmess.io.fd, io_events_to_libev(events)); ev_io_start(loop->loop, &io->libevmess.io); ev_unref(loop->loop); } else { ev_io_set(&io->libevmess.io, io->libevmess.io.fd, io_events_to_libev(events)); } } void li_event_io_add_events(liEventIO *io, int events) { li_event_io_set_events(io, io->events | events); } void li_event_io_rem_events(liEventIO *io, int events) { li_event_io_set_events(io, io->events & ~events); } static void event_timer_cb(struct ev_loop *loop, ev_timer *w, int revents) { liEventTimer *timer = LI_CONTAINER_OF(w, liEventTimer, libevmess.timer); liEventLoop *my_loop = timer->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); if (ev_is_active(w)) { if (!timer->base.keep_loop_alive) ev_ref(loop); ev_timer_stop(loop, w); } timer->base.active = 0; timer->base.callback(&timer->base, LI_EV_WAKEUP); } void li_event_timer_init(liEventLoop *loop, liEventTimer *timer, liEventCallback callback) { memset(timer, 0, sizeof(*timer)); timer->base.type = LI_EVT_TIMER; timer->base.keep_loop_alive = 1; timer->base.callback = callback; ev_init(&timer->libevmess.w, NULL); ev_set_cb(&timer->libevmess.timer, event_timer_cb); if (NULL != loop) li_event_attach(loop, timer); } static void event_async_cb(struct ev_loop *loop, ev_async *w, int revents) { liEventAsync *async = LI_CONTAINER_OF(w, liEventAsync, libevmess.async); liEventLoop *my_loop = async->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); async->base.callback(&async->base, LI_EV_WAKEUP); } void li_event_async_init(liEventLoop *loop, liEventAsync *async, liEventCallback callback) { memset(async, 0, sizeof(*async)); async->base.type = LI_EVT_ASYNC; async->base.keep_loop_alive = 0; async->base.callback = callback; ev_init(&async->libevmess.w, NULL); ev_set_cb(&async->libevmess.async, event_async_cb); if (NULL != loop) li_event_attach(loop, async); li_event_start(async); } static void event_child_cb(struct ev_loop *loop, ev_child *w, int revents) { liEventChild *child = LI_CONTAINER_OF(w, liEventChild, libevmess.child); liEventLoop *my_loop = child->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); if (ev_is_active(w)) { if (!child->base.keep_loop_alive) ev_ref(loop); ev_child_stop(loop, w); } child->base.active = 0; child->base.callback(&child->base, LI_EV_WAKEUP); } void li_event_child_init(liEventLoop *loop, liEventChild *child, liEventCallback callback, int pid) { memset(child, 0, sizeof(*child)); child->base.type = LI_EVT_CHILD; child->base.keep_loop_alive = 1; child->base.callback = callback; ev_init(&child->libevmess.w, NULL); ev_child_set(&child->libevmess.child, pid, 0); ev_set_cb(&child->libevmess.child, event_child_cb); if (NULL != loop) li_event_attach(loop, child); li_event_start(child); } static void event_signal_cb(struct ev_loop *loop, ev_signal *w, int revents) { liEventSignal *sig = LI_CONTAINER_OF(w, liEventSignal, libevmess.sig); liEventLoop *my_loop = sig->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); sig->base.callback(&sig->base, LI_EV_WAKEUP); } void li_event_signal_init(liEventLoop *loop, liEventSignal *sig, liEventCallback callback, int signum) { memset(sig, 0, sizeof(*sig)); sig->base.type = LI_EVT_SIGNAL; sig->base.keep_loop_alive = 0; sig->base.callback = callback; ev_init(&sig->libevmess.w, NULL); ev_signal_set(&sig->libevmess.sig, signum); ev_set_cb(&sig->libevmess.sig, event_signal_cb); if (NULL != loop) li_event_attach(loop, sig); li_event_start(sig); } static void event_prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { liEventPrepare *prepare = LI_CONTAINER_OF(w, liEventPrepare, libevmess.prepare); liEventLoop *my_loop = prepare->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); prepare->base.callback(&prepare->base, LI_EV_WAKEUP); } void li_event_prepare_init(liEventLoop *loop, liEventPrepare *prepare, liEventCallback callback) { memset(prepare, 0, sizeof(*prepare)); prepare->base.type = LI_EVT_PREPARE; prepare->base.keep_loop_alive = 0; prepare->base.callback = callback; ev_init(&prepare->libevmess.w, NULL); ev_set_cb(&prepare->libevmess.prepare, event_prepare_cb); if (NULL != loop) li_event_attach(loop, prepare); li_event_start(prepare); } static void event_check_cb(struct ev_loop *loop, ev_check *w, int revents) { liEventCheck *check = LI_CONTAINER_OF(w, liEventCheck, libevmess.check); liEventLoop *my_loop = check->base.link_watchers.data; UNUSED(revents); assert(NULL != my_loop); assert(loop == my_loop->loop); check->base.callback(&check->base, LI_EV_WAKEUP); } void li_event_check_init(liEventLoop *loop, liEventCheck *check, liEventCallback callback) { memset(check, 0, sizeof(*check)); check->base.type = LI_EVT_CHECK; check->base.keep_loop_alive = 0; check->base.callback = callback; ev_init(&check->libevmess.w, NULL); ev_set_cb(&check->libevmess.check, event_check_cb); if (NULL != loop) li_event_attach(loop, check); li_event_start(check); }