/*
* ev.h
*/
#ifndef EV_H_
#define EV_H_
#define EV_FEATURES 0x7f
#define EV_FEATURE_CONFIG ((EV_FEATURES) & 4)
#define EV_MINPRI (EV_FEATURE_CONFIG ? -2 : 0)
#define EV_MAXPRI (EV_FEATURE_CONFIG ? +2 : 0)
#define NUMPRI (EV_MAXPRI - EV_MINPRI + 1)
#define ABSPRI(w) (((W)w)->priority - EV_MINPRI)
#define EV_CB_INVOKE(watcher, revents) (watcher)->cb ((watcher), (revents))
#define EMPTY2(a,b) /* used to suppress some warnings */
/************* watcher related ***********************/
typedef struct ev_watcher
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_watcher *w, int revents);
struct ev_watcher *next;
} ev_watcher;
typedef ev_watcher *W;
typedef struct ev_watcher_list
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_watcher_list *w, int revents);
struct ev_watcher_list *next;
} ev_watcher_list;
typedef ev_watcher_list *WL;
/* invoked when fd is either EV_READable or EV_WRITEable */
/* revent EV_READ, EV_WRITE */
typedef struct ev_io
{
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_io *w, int revents);
struct ev_io *next;
int fd; /* ro */
int events; /* ro */
} ev_io;
/* stores the pending event set for a given watcher */
typedef struct
{
W w;
int events; /* the pending event set for the given watcher */
} ANPENDING;
int pendingpri;
int pendingcnt[NUMPRI];
ANPENDING *pendings [NUMPRI]; // save pending events
int pendingmax [NUMPRI];
/**************** ANFD related ****************************/
/* set in reify when reification needed */
#define EV_ANFD_REIFY 1
/* file descriptor info structure */
typedef struct
{
WL head;
unsigned char events; /* the events watched for */
unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */
unsigned char emask; /* the epoll backend stores the actual kernel mask in here */
unsigned char unused;
unsigned int egen; /* generation counter to counter epoll bugs */
} ANFD;
ANFD * anfds; // save all events
int anfdmax;
int * fdchanges;
int fdchangemax;
int fdchangecnt;
/***************** function interface *****************************/
void fd_event(int fd, int revents);
void ev_invoke_pending();
void ev_io_start (ev_io *w);
void ev_io_stop (ev_io *w);
#endif /* EV_H_ */
#include "ev.h"
#include <stdio.h>
#include <stdlib.h>
/*****************************************************************************/
void * ev_realloc (void *ptr, long size)
{
ptr = realloc(ptr, size);
if (!ptr && size)
{
abort();
}
return ptr;
}
#define MALLOC_ROUND 4096 /* prefer to allocate in chunks of this size, must be 2**n and >> 4 longs */
/* find a suitable new size for the given array, */
/* hopefully by rounding to a nice-to-malloc size */
int array_nextsize (int elem, int cur, int cnt)
{
int ncur = cur + 1;
do
ncur <<= 1;
while (cnt > ncur);
/* if size is large, round to MALLOC_ROUND - 4 * longs to accommodate malloc overhead */
if (elem * ncur > MALLOC_ROUND - sizeof (void *) * 4)
{
ncur *= elem;
ncur = (ncur + elem + (MALLOC_ROUND - 1) + sizeof (void *) * 4) & ~(MALLOC_ROUND - 1);
ncur = ncur - sizeof (void *) * 4;
ncur /= elem;
}
return ncur;
}
static void * array_realloc (int elem, void *base, int *cur, int cnt)
{
*cur = array_nextsize (elem, *cur, cnt);
return ev_realloc (base, elem * *cur);
}
#define array_init_zero(base,count) \
memset ((void *)(base), 0, sizeof (*(base)) * (count))
#define array_needsize(type,base,cur,cnt,init) \
if (0 != ((cnt) > (cur))) \
{ \
int ocur_ = (cur); \
(base) = (type *)array_realloc \
(sizeof (type), (base), &(cur), (cnt)); \
init ((base) + (ocur_), (cur) - ocur_); \
}
#define array_free(stem, idx) \
ev_free (stem ## s idx); stem ## cnt idx = stem ## max idx = 0; stem ## s idx = 0
/*****************************************************************************/
void ev_invoke_pending()
{
pendingpri = NUMPRI;
while (pendingpri) /* pendingpri possibly gets modified in the inner loop */
{
--pendingpri;
while (pendingcnt[pendingpri])
{
ANPENDING *p = pendings[pendingpri] + --pendingcnt[pendingpri];
p->w->pending = 0;
EV_CB_INVOKE(p->w, p->events);
}
}
}
void ev_feed_event(void *w, int revents)
{
W w_ = (W) w;
int pri = ABSPRI(w_);
if (0 != (w_->pending))
{
pendings[pri][w_->pending - 1].events |= revents;
}
else
{
w_->pending = ++pendingcnt[pri];
array_needsize(ANPENDING, pendings[pri], pendingmax[pri],
w_->pending, EMPTY2);
pendings[pri][w_->pending - 1].w = w_;
pendings[pri][w_->pending - 1].events = revents;
}
pendingpri = NUMPRI - 1;
}
void fd_event_nocheck(int fd, int revents)
{
ANFD *anfd = anfds + fd;
ev_io *w;
for (w = (ev_io *) anfd->head; w; w = (ev_io *) ((WL) w)->next)
{
int ev = w->events & revents;
if (ev)
ev_feed_event((W) w, ev);
}
}
/* do not submit kernel events for fds that have reify set */
/* because that means they changed while we were polling for new events */
void fd_event(int fd, int revents)
{
ANFD *anfd = anfds + fd;
if (!anfd->reify)
fd_event_nocheck(fd, revents);
}
void wlist_add (WL *head, WL elem)
{
elem->next = *head;
*head = elem;
}
void wlist_del (WL *head, WL elem)
{
while (*head)
{
if (*head == elem)
{
*head = elem->next;
break;
}
head = &(*head)->next;
}
}
void ev_io_start (ev_io *w)
{
int fd = w->fd;
array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
wlist_add (&anfds[fd].head, (WL)w);
}
void ev_io_stop (ev_io *w)
{
wlist_del (&anfds[w->fd].head, (WL)w);
}
/*
* test.c
*
*/
#include "ev.h"
#include <stdio.h>
void test_cb(struct ev_watcher *w, int revents)
{
printf("test_cb\n");
}
// 1 fd -> multi events(or event watcher)
// epoll_poll -> fd_event -> fd_event_nocheck -> ev_feed_event -> ev_invoke_pending
int main(int aargc, char *argv[])
{
ev_io w_io;
w_io.active = 1;
w_io.pending = 0;
w_io.priority = 2;
w_io.data = 0;
w_io.cb = test_cb;
w_io.next = 0;
w_io.fd = 10;
w_io.events = 2;
ev_io_start(&w_io); // register event
int fd = 10;
int revents = 2;
fd_event(fd, revents); // discover event
ev_invoke_pending(); // raise event
return 0;
}