From cc75a05531d9d05ac14fdaec7960b1c970c87bbb Mon Sep 17 00:00:00 2001 From: root Date: Tue, 30 Oct 2007 20:59:31 +0000 Subject: initial roguh cut that doesn't immediately barf --- README | 9 ++ ev.c | 492 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ev.h | 85 +++++++++++ ev_epoll.c | 89 +++++++++++ ev_select.c | 0 5 files changed, 675 insertions(+) create mode 100644 README create mode 100644 ev.c create mode 100644 ev.h create mode 100644 ev_epoll.c create mode 100644 ev_select.c diff --git a/README b/README new file mode 100644 index 0000000..4e8ec2d --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +libev is modelled after libevent (http://monkey.org/~provos/libevent/), but aims +to be faster and more correct, and also more featureful. Examples: + +- multiple watchers can wait for the same event without deregistering others. +- fork() is supported and can be handled. +- timers are handled as a priority queue (faster) +- watchers use less memory (faster) +- less calls to epoll_ctl (faster) + diff --git a/ev.c b/ev.c new file mode 100644 index 0000000..898cdeb --- /dev/null +++ b/ev.c @@ -0,0 +1,492 @@ +#include +#include + +#include + +#include +#include +#include + +#ifdef CLOCK_MONOTONIC +# define HAVE_MONOTONIC 1 +#endif + +#define HAVE_EPOLL 1 +#define HAVE_REALTIME 1 +#define HAVE_SELECT 0 + +#define MAX_BLOCKTIME 60. + +#include "ev.h" + +struct ev_watcher { + EV_WATCHER (ev_watcher); +}; + +struct ev_watcher_list { + EV_WATCHER_LIST (ev_watcher_list); +}; + +ev_tstamp ev_now; +int ev_method; + +static int have_monotonic; /* runtime */ + +static ev_tstamp method_fudge; /* stupid epoll-returns-early bug */ +static void (*method_reify)(void); +static void (*method_poll)(ev_tstamp timeout); + +ev_tstamp +ev_time (void) +{ +#if HAVE_REALTIME + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + return ts.tv_sec + ts.tv_nsec * 1e-9; +#else + struct timeval tv; + gettimeofday (&tv, 0); + return tv.tv_sec + tv.tv_usec * 1e-6; +#endif +} + +static ev_tstamp +get_clock (void) +{ +#if HAVE_MONOTONIC + if (have_monotonic) + { + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec * 1e-9; + } +#endif + + return ev_time (); +} + +#define array_needsize(base,cur,cnt,init) \ + if ((cnt) > cur) \ + { \ + int newcnt = cur; \ + do \ + { \ + newcnt += (newcnt >> 1) + 16; \ + } \ + while ((cnt) > newcnt); \ + fprintf (stderr, "resize(" # base ") from %d to %d\n", cur, newcnt);\ + base = realloc (base, sizeof (*base) * (newcnt)); \ + init (base + cur, newcnt - cur); \ + cur = newcnt; \ + } + +typedef struct +{ + struct ev_io *head; + unsigned char wev, rev; /* want, received event set */ +} ANFD; + +static ANFD *anfds; +static int anfdmax; + +static int *fdchanges; +static int fdchangemax, fdchangecnt; + +static void +anfds_init (ANFD *base, int count) +{ + while (count--) + { + base->head = 0; + base->wev = base->rev = EV_NONE; + ++base; + } +} + +typedef struct +{ + struct ev_watcher *w; + int events; +} ANPENDING; + +static ANPENDING *pendings; +static int pendingmax, pendingcnt; + +static void +event (struct ev_watcher *w, int events) +{ + w->pending = ++pendingcnt; + array_needsize (pendings, pendingmax, pendingcnt, ); + pendings [pendingcnt - 1].w = w; + pendings [pendingcnt - 1].events = events; +} + +static void +fd_event (int fd, int events) +{ + ANFD *anfd = anfds + fd; + struct ev_io *w; + + for (w = anfd->head; w; w = w->next) + { + int ev = w->events & events; + + if (ev) + event ((struct ev_watcher *)w, ev); + } +} + +static struct ev_timer **timers; +static int timermax, timercnt; + +static void +upheap (int k) +{ + struct ev_timer *w = timers [k]; + + while (k && timers [k >> 1]->at > w->at) + { + timers [k] = timers [k >> 1]; + timers [k]->active = k + 1; + k >>= 1; + } + + timers [k] = w; + timers [k]->active = k + 1; + +} + +static void +downheap (int k) +{ + struct ev_timer *w = timers [k]; + + while (k <= (timercnt >> 1)) + { + int j = k << 1; + + if (j + 1 < timercnt && timers [j]->at > timers [j + 1]->at) + ++j; + + if (w->at <= timers [j]->at) + break; + + timers [k] = timers [j]; + timers [k]->active = k; + k = j; + } + + timers [k] = w; + timers [k]->active = k + 1; +} + +static struct ev_signal **signals; +static int signalmax, signalcnt; + +static void +signals_init (struct ev_signal **base, int count) +{ + while (count--) + *base++ = 0; +} + +#if HAVE_EPOLL +# include "ev_epoll.c" +#endif +#if HAVE_SELECT +# include "ev_select.c" +#endif + +int ev_init (int flags) +{ +#if HAVE_MONOTONIC + { + struct timespec ts; + if (!clock_gettime (CLOCK_MONOTONIC, &ts)) + have_monotonic = 1; + } +#endif + + ev_now = ev_time (); + +#if HAVE_EPOLL + if (epoll_init (flags)) + return ev_method; +#endif +#if HAVE_SELECT + if (select_init (flags)) + return ev_method; +#endif + + ev_method = EVMETHOD_NONE; + return ev_method; +} + +void ev_prefork (void) +{ +} + +void ev_postfork_parent (void) +{ +} + +void ev_postfork_child (void) +{ +#if HAVE_EPOLL + epoll_postfork_child (); +#endif +} + +static void +call_pending () +{ + int i; + + for (i = 0; i < pendingcnt; ++i) + { + ANPENDING *p = pendings + i; + + if (p->w) + { + p->w->pending = 0; + p->w->cb (p->w, p->events); + } + } + + pendingcnt = 0; +} + +static void +timer_reify (void) +{ + while (timercnt && timers [0]->at <= ev_now) + { + struct ev_timer *w = timers [0]; + + /* first reschedule timer */ + if (w->repeat) + { + fprintf (stderr, "a %f now %f repeat %f, %f\n", w->at, ev_now, w->repeat, w->repeat *1e30);//D + if (w->is_abs) + w->at += floor ((ev_now - w->at) / w->repeat + 1.) * w->repeat; + else + w->at = ev_now + w->repeat; + + fprintf (stderr, "b %f\n", w->at);//D + + downheap (0); + } + else + evtimer_stop (w); /* nonrepeating: stop timer */ + + event ((struct ev_watcher *)w, EV_TIMEOUT); + } +} + +int ev_loop_done; + +int ev_loop (int flags) +{ + double block; + ev_loop_done = flags & EVLOOP_ONESHOT; + + do + { + /* update fd-related kernel structures */ + method_reify (); fdchangecnt = 0; + + /* calculate blocking time */ + ev_now = ev_time (); + + if (flags & EVLOOP_NONBLOCK) + block = 0.; + else if (!timercnt) + block = MAX_BLOCKTIME; + else + { + block = timers [0]->at - ev_now + method_fudge; + if (block < 0.) block = 0.; + else if (block > MAX_BLOCKTIME) block = MAX_BLOCKTIME; + } + + fprintf (stderr, "block %f\n", block);//D + method_poll (block); + + /* put pending timers into pendign queue and reschedule them */ + timer_reify (); + + ev_now = ev_time (); + call_pending (); + } + while (!ev_loop_done); +} + +static void +wlist_add (struct ev_watcher_list **head, struct ev_watcher_list *elem) +{ + elem->next = *head; + *head = elem; +} + +static void +wlist_del (struct ev_watcher_list **head, struct ev_watcher_list *elem) +{ + while (*head) + { + if (*head == elem) + { + *head = elem->next; + return; + } + + head = &(*head)->next; + } +} + +static void +ev_start (struct ev_watcher *w, int active) +{ + w->pending = 0; + w->active = active; +} + +static void +ev_stop (struct ev_watcher *w) +{ + if (w->pending) + pendings [w->pending - 1].w = 0; + + w->active = 0; + /* nop */ +} + +void +evio_start (struct ev_io *w) +{ + if (ev_is_active (w)) + return; + + int fd = w->fd; + + ev_start ((struct ev_watcher *)w, 1); + array_needsize (anfds, anfdmax, fd + 1, anfds_init); + wlist_add ((struct ev_watcher_list **)&anfds[fd].head, (struct ev_watcher_list *)w); + + ++fdchangecnt; + array_needsize (fdchanges, fdchangemax, fdchangecnt, ); + fdchanges [fdchangecnt - 1] = fd; +} + +void +evio_stop (struct ev_io *w) +{ + if (!ev_is_active (w)) + return; + + wlist_del ((struct ev_watcher_list **)&anfds[w->fd].head, (struct ev_watcher_list *)w); + ev_stop ((struct ev_watcher *)w); + + ++fdchangecnt; + array_needsize (fdchanges, fdchangemax, fdchangecnt, ); + fdchanges [fdchangecnt - 1] = w->fd; +} + +void +evtimer_start (struct ev_timer *w) +{ + if (ev_is_active (w)) + return; + + fprintf (stderr, "t1 %f a %d\n", w->at, w->is_abs);//D + if (w->is_abs) + { + if (w->repeat) + w->at += ceil ((ev_now - w->at) / w->repeat) * w->repeat; + } + else + w->at += ev_now; + fprintf (stderr, "t2 %f a %d\n", w->at, w->is_abs);//D + + ev_start ((struct ev_watcher *)w, ++timercnt); + array_needsize (timers, timermax, timercnt, ); + timers [timercnt - 1] = w; + upheap (timercnt - 1); +} + +void +evtimer_stop (struct ev_timer *w) +{ + if (!ev_is_active (w)) + return; + + timers [w->active - 1] = timers [--timercnt]; + downheap (w->active - 1); + ev_stop ((struct ev_watcher *)w); +} + +void +evsignal_start (struct ev_signal *w) +{ + if (ev_is_active (w)) + return; + + ev_start ((struct ev_watcher *)w, 1); + array_needsize (signals, signalmax, w->signum, signals_init); + wlist_add ((struct ev_watcher_list **)&signals [w->signum - 1], (struct ev_watcher_list *)w); +} + +void +evsignal_stop (struct ev_signal *w) +{ + if (!ev_is_active (w)) + return; + + wlist_del ((struct ev_watcher_list **)&signals [w->signum - 1], (struct ev_watcher_list *)w); + ev_stop ((struct ev_watcher *)w); +} + +/*****************************************************************************/ +#if 1 + +static void +sin_cb (struct ev_io *w, int revents) +{ + fprintf (stderr, "sin %d, revents %d\n", w->fd, revents); +} + +static void +ocb (struct ev_timer *w, int revents) +{ + fprintf (stderr, "timer %f,%f (%x) (%f) d%p\n", w->at, w->repeat, revents, w->at - ev_time (), w->data); +} + +int main (void) +{ + struct ev_io sin; + + ev_init (0); + + evw_init (&sin, sin_cb, 55); + evio_set (&sin, 0, EV_READ); + evio_start (&sin); + + struct ev_timer t1; + evw_init (&t1, ocb, 1); + evtimer_set_rel (&t1, 1, 0); + evtimer_start (&t1); + + struct ev_timer t2; + evw_init (&t2, ocb, 2); + evtimer_set_abs (&t2, ev_time () + 2, 0); + evtimer_start (&t2); + + ev_loop (0); + + return 0; +} + +#endif + + + + diff --git a/ev.h b/ev.h new file mode 100644 index 0000000..300573b --- /dev/null +++ b/ev.h @@ -0,0 +1,85 @@ +#ifndef EV_H +#define EV_H + +typedef double ev_tstamp; + +/* eventmask, revents, events... */ +#define EV_UNDEF -1 /* guaranteed to be invalid */ +#define EV_NONE 0 +#define EV_READ 1 +#define EV_WRITE 2 +#define EV_TIMEOUT 4 +#define EV_SIGNAL 8 + +/* shared by all watchers */ +#define EV_WATCHER(type) \ + int active; /* private */ \ + int pending; /* private */ \ + void *data; /* rw */ \ + void (*cb)(struct type *, int revents) /* rw */ + +#define EV_WATCHER_LIST(type) \ + EV_WATCHER (type); \ + struct type *next /* private */ + +struct ev_timer +{ + EV_WATCHER_LIST (ev_timer); + + ev_tstamp at; /* ro */ + ev_tstamp repeat; /* rw */ + unsigned char is_abs; /* rw */ +}; + +struct ev_io +{ + EV_WATCHER_LIST (ev_io); + + int fd; /* ro */ + int events; /* ro */ +}; + +struct ev_signal +{ + EV_WATCHER_LIST (ev_signal); + + int signum; /* ro */ +}; + +#define EVMETHOD_NONE 0 +#define EVMETHOD_SELECT 1 +#define EVMETHOD_EPOLL 2 +int ev_init (int flags); +extern int ev_method; + +void ev_prefork (void); +void ev_postfork_parent (void); +void ev_postfork_child (void); + +extern ev_tstamp ev_now; /* time w.r.t. timers and the eventloop, updated after each poll */ +ev_tstamp ev_time (void); + +#define EVLOOP_NONBLOCK 1 /* do not block/wait */ +#define EVLOOP_ONESHOT 2 /* block *once* only */ +int ev_loop (int flags); +extern int ev_loop_done; /* set to 1 to break out of event loop */ + +#define evw_init(ev,cb_,data_) do { (ev)->active = 0; (ev)->cb = (cb_); (ev)->data = (void *)data_; } while (0) +#define evio_set(ev,fd_,events_) do { (ev)->fd = (fd_); (ev)->events = (events_); } while (0) +#define evtimer_set_rel(ev,after_,repeat_) do { (ev)->at = (after_); (ev)->repeat = (repeat_); (ev)->is_abs = 0; } while (0) +#define evtimer_set_abs(ev,at_,repeat_) do { (ev)->at = (at_); (ev)->repeat = (repeat_); (ev)->is_abs = 1; } while (0) +#define evsignal_set(ev,signum_) do { (ev)->signum = (signum_); } while (0) + +#define ev_is_active(ev) (0 + (ev)->active) /* wether the watcher has been started */ + +void evio_start (struct ev_io *w); +void evio_stop (struct ev_io *w); + +void evtimer_start (struct ev_timer *w); +void evtimer_stop (struct ev_timer *w); + +void evsignal_start (struct ev_signal *w); +void evsignal_stop (struct ev_signal *w); + +#endif + diff --git a/ev_epoll.c b/ev_epoll.c new file mode 100644 index 0000000..42e4a8b --- /dev/null +++ b/ev_epoll.c @@ -0,0 +1,89 @@ +#include + +static int epoll_fd = -1; + +static void +epoll_reify_fd (int fd) +{ + ANFD *anfd = anfds + fd; + struct ev_io *w; + + int wev = 0; + + for (w = anfd->head; w; w = w->next) + wev |= w->events; + + if (anfd->wev != wev) + { + int mode = wev ? anfd->wev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD : EPOLL_CTL_DEL; + struct epoll_event ev; + ev.events = wev; + ev.data.fd = fd; + fprintf (stderr, "reify %d,%d,%d m%d (r=%d)\n", fd, anfd->wev, wev, mode,//D + epoll_ctl (epoll_fd, mode, fd, &ev) + );//D + anfd->wev = wev; + } +} + +void epoll_postfork_child (void) +{ + int i; + + epoll_fd = epoll_create (256); + + for (i = 0; i < anfdmax; ++i) + epoll_reify_fd (i); +} + +static void epoll_reify (void) +{ + int i; + for (i = 0; i < fdchangecnt; ++i) + epoll_reify_fd (fdchanges [i]); +} + +static struct epoll_event *events; +static int eventmax; + +static void epoll_poll (ev_tstamp timeout) +{ + int eventcnt = epoll_wait (epoll_fd, events, eventmax, ceil (timeout * 1000.)); + int i; + + if (eventcnt < 0) + return; + + for (i = 0; i < eventcnt; ++i) + fd_event ( + events [i].data.fd, + (events [i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLPRI) ? EV_WRITE : 0) + | (events [i].events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0) + ); + + /* if the receive array was full, increase its size */ + if (eventcnt == eventmax) + { + free (events); + eventmax += eventmax >> 1; + events = malloc (sizeof (struct epoll_event) * eventmax); + } +} + +int epoll_init (int flags) +{ + epoll_fd = epoll_create (256); + + if (epoll_fd < 0) + return 0; + + ev_method = EVMETHOD_EPOLL; + method_fudge = 1e-3; /* needed to compensate fro epoll returning early */ + method_reify = epoll_reify; + method_poll = epoll_poll; + + eventmax = 64; /* intiial number of events receivable per poll */ + events = malloc (sizeof (struct epoll_event) * eventmax); + + return 1; +} diff --git a/ev_select.c b/ev_select.c new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3