#define DEFAULT_EPOLL_TIMEOUT 1000 /* 1 sec */ static inline uint64_t get_current_msec() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000 + ts.tv_nsec / 1000 / 1000; } static inline int64_t run_timers(list_head_t * timer_list) { ev_timer_t *timer; int64_t timeout = DEFAULT_EPOLL_TIMEOUT; while (!list_empty(timer_list) && (timer = list_first_entry(timer_list, ev_timer_t, list))) { timeout = timer->abs_msec - get_current_msec(); if (timeout <= 0) { /* expired */ timer->callback(timer); list_del(&timer->list); timeout = DEFAULT_EPOLL_TIMEOUT; } else { break; } } return timeout > DEFAULT_EPOLL_TIMEOUT ? DEFAULT_EPOLL_TIMEOUT : timeout; } void ev_destory_context(ev_context_t * c) { close(c->efd); free(c); } ev_context_t *ev_create_context(int max_events) { size_t size; ev_context_t *c; assert(max_events > 0); size = sizeof(ev_context_t) + max_events * sizeof(struct epoll_event); c = calloc(size, 1); if (!c) return NULL; c->max_events = max_events; c->stopped = 0; INIT_LIST_HEAD(&c->timer_list); c->efd = epoll_create(max_events); if (c->efd == -1) { free(c); return NULL; } return c; } int ev_run(ev_context_t * c) { int nfds; int64_t timeout; struct epoll_event *ev; ev_event_t *event; int n; while (!c->stopped) { timeout = run_timers(&c->timer_list); nfds = epoll_wait(c->efd, c->events, c->max_events, timeout); if (nfds == -1) { if (errno == EINTR) continue; return -1; /* exit, should be never to here */ } for (n = 0; n < nfds; ++n) { ev = &c->events[n]; event = (ev_event_t *) (ev->data.ptr); event->callback(event); } } return 0; } int ev_register_event(ev_context_t * c, ev_event_t * event) { struct epoll_event ev; ev.data.ptr = (void *)event; ev.events = event->events; return epoll_ctl(c->efd, EPOLL_CTL_ADD, event->fd, &ev); } void ev_unregister_event(ev_context_t * c, ev_event_t * event) { epoll_ctl(c->efd, EPOLL_CTL_DEL, event->fd, NULL); } void ev_init_timer(ev_timer_t * timer, uint64_t msec, ev_timer_callback_t callback) { timer->callback = callback; INIT_LIST_NODE(&timer->list); timer->msec = msec; } void ev_start_timer(ev_context_t * c, ev_timer_t * timer) { ev_timer_t *t; timer->abs_msec = get_current_msec() + timer->msec; list_for_each_entry(t, &c->timer_list, list) { if (t->abs_msec > timer->abs_msec) { __list_add(&timer->list, t->list.prev, &t->list); run_timers(&c->timer_list); return; } } list_add_tail(&timer->list, &c->timer_list); run_timers(&c->timer_list); } void ev_cancel_timer(ev_context_t * c, ev_timer_t * timer) { UNUSED(c); list_del(&timer->list); }