diff options
| -rw-r--r-- | README | 9 | ||||
| -rw-r--r-- | ev.c | 492 | ||||
| -rw-r--r-- | ev.h | 85 | ||||
| -rw-r--r-- | ev_epoll.c | 89 | ||||
| -rw-r--r-- | ev_select.c | 0 | 
5 files changed, 675 insertions, 0 deletions
| @@ -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) + @@ -0,0 +1,492 @@ +#include <math.h> +#include <stdlib.h> + +#include <stdio.h> + +#include <errno.h> +#include <sys/time.h> +#include <time.h> + +#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 + + + + @@ -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 <sys/epoll.h> + +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 --- /dev/null +++ b/ev_select.c | 
