#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#ifdef WIN32
#include <winsock2.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <windows.h>
#include <time.h>
#else
#include <poll.h>
#include <sys/time.h>
#endif
#include "cat_ae.h"
#if defined(HAVE_EVPORT)
#include "ae_evport.h"
#elif defined(HAVE_EPOLL)
#include "ae_epoll.h"
#elif defined(HAVE_KQUEUE)
#include "ae_kqueue.h"
#else
#include "ae_select.h"
#endif
aeEventLoop *catAeCreateEventLoop(int setsize) {
aeEventLoop *eventLoop;
int i;
if ((eventLoop = malloc(sizeof(*eventLoop))) == NULL) goto err;
eventLoop->events = malloc(sizeof(aeFileEvent) * setsize);
eventLoop->fired = malloc(sizeof(aeFiredEvent) * setsize);
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
eventLoop->setsize = setsize;
eventLoop->lastTime = time(NULL);
eventLoop->timeEventHead = NULL;
eventLoop->timeEventNextId = 0;
eventLoop->stop = 0;
eventLoop->maxfd = -1;
eventLoop->beforesleep = NULL;
if (aeApiCreate(eventLoop) == -1) goto err;
for (i = 0; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return eventLoop;
err:
if (eventLoop) {
free(eventLoop->events);
free(eventLoop->fired);
free(eventLoop);
}
return NULL;
}
int catAeGetSetSize(aeEventLoop *eventLoop) {
return eventLoop->setsize;
}
int catAeResizeSetSize(aeEventLoop *eventLoop, int setsize) {
int i;
if (setsize == eventLoop->setsize) return AE_OK;
if (eventLoop->maxfd >= setsize) return AE_ERR;
if (aeApiResize(eventLoop, setsize) == -1) return AE_ERR;
eventLoop->events = realloc(eventLoop->events, sizeof(aeFileEvent) * setsize);
eventLoop->fired = realloc(eventLoop->fired, sizeof(aeFiredEvent) * setsize);
eventLoop->setsize = setsize;
for (i = eventLoop->maxfd + 1; i < setsize; i++)
eventLoop->events[i].mask = AE_NONE;
return AE_OK;
}
void catAeDeleteEventLoop(aeEventLoop *eventLoop) {
aeApiFree(eventLoop);
free(eventLoop->events);
free(eventLoop->fired);
free(eventLoop);
}
void catAeStop(aeEventLoop *eventLoop) {
eventLoop->stop = 1;
}
int catAeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData) {
if (fd >= eventLoop->setsize) {
errno = ERANGE;
return AE_ERR;
}
aeFileEvent *fe = &eventLoop->events[fd];
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
fe->mask |= mask;
if (mask & AE_READABLE) fe->rfileProc = proc;
if (mask & AE_WRITABLE) fe->wfileProc = proc;
fe->clientData = clientData;
if (fd > eventLoop->maxfd)
eventLoop->maxfd = fd;
return AE_OK;
}
void catAeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask) {
aeFileEvent *fe = NULL;
if (fd >= eventLoop->setsize) return;
fe = &eventLoop->events[fd];
if (fe->mask == AE_NONE) return;
aeApiDelEvent(eventLoop, fd, mask);
fe->mask = fe->mask & (~mask);
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
int j;
for (j = eventLoop->maxfd - 1; j >= 0; j--)
if (eventLoop->events[j].mask != AE_NONE) break;
eventLoop->maxfd = j;
}
}
int catAeGetFileEvents(aeEventLoop *eventLoop, int fd) {
aeFileEvent *fe = NULL;
if (fd >= eventLoop->setsize) return 0;
fe = &eventLoop->events[fd];
return fe->mask;
}
static void aeGetTime(long *seconds, long *milliseconds) {
#ifdef WIN32
struct _timeb timeBuf;
_ftime64_s(&timeBuf);
*seconds = (timeBuf.time);
*milliseconds = (timeBuf.millitm);
#else
struct timeval tv;
gettimeofday(&tv, NULL);
*seconds = tv.tv_sec;
*milliseconds = tv.tv_usec / 1000;
#endif
}
static void aeAddMillisecondsToNow(long long milliseconds, long *sec, long *ms) {
long cur_sec, cur_ms, when_sec, when_ms;
aeGetTime(&cur_sec, &cur_ms);
when_sec = cur_sec + milliseconds / 1000;
when_ms = cur_ms + milliseconds % 1000;
if (when_ms >= 1000) {
when_sec++;
when_ms -= 1000;
}
*sec = when_sec;
*ms = when_ms;
}
long long catAeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
aeTimeProc *proc, void *clientData,
aeEventFinalizerProc *finalizerProc) {
long long id = eventLoop->timeEventNextId++;
aeTimeEvent *te;
te = malloc(sizeof(*te));
if (te == NULL) return AE_ERR;
te->id = id;
aeAddMillisecondsToNow(milliseconds, &te->when_sec, &te->when_ms);
te->timeProc = proc;
te->finalizerProc = finalizerProc;
te->clientData = clientData;
te->next = eventLoop->timeEventHead;
eventLoop->timeEventHead = te;
return id;
}
int catAeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) {
aeTimeEvent *te, *prev = NULL;
te = eventLoop->timeEventHead;
while (te) {
if (te->id == id) {
if (prev == NULL)
eventLoop->timeEventHead = te->next;
else
prev->next = te->next;
if (te->finalizerProc)
te->finalizerProc(eventLoop, te->clientData);
free(te);
return AE_OK;
}
prev = te;
te = te->next;
}
return AE_ERR;
}
static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop) {
aeTimeEvent *te = eventLoop->timeEventHead;
aeTimeEvent *nearest = NULL;
while (te) {
if (!nearest || te->when_sec < nearest->when_sec ||
(te->when_sec == nearest->when_sec &&
te->when_ms < nearest->when_ms))
nearest = te;
te = te->next;
}
return nearest;
}
static int processTimeEvents(aeEventLoop *eventLoop) {
int processed = 0;
aeTimeEvent *te;
long long maxId;
time_t now = time(NULL);
if (now < eventLoop->lastTime) {
te = eventLoop->timeEventHead;
while (te) {
te->when_sec = 0;
te = te->next;
}
}
eventLoop->lastTime = now;
te = eventLoop->timeEventHead;
maxId = eventLoop->timeEventNextId - 1;
while (te) {
long now_sec, now_ms;
long long id;
if (te->id > maxId) {
te = te->next;
continue;
}
aeGetTime(&now_sec, &now_ms);
if (now_sec > te->when_sec ||
(now_sec == te->when_sec && now_ms >= te->when_ms)) {
int retval;
id = te->id;
retval = te->timeProc(eventLoop, id, te->clientData);
processed++;
if (retval != AE_NOMORE) {
aeAddMillisecondsToNow(retval, &te->when_sec, &te->when_ms);
} else {
catAeDeleteTimeEvent(eventLoop, id);
}
te = eventLoop->timeEventHead;
} else {
te = te->next;
}
}
return processed;
}
int catAeProcessEvents(aeEventLoop *eventLoop, int flags) {
int processed = 0, numevents;
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
if (eventLoop->maxfd != -1 ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
aeTimeEvent *shortest = NULL;
struct timeval tv, *tvp;
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
shortest = aeSearchNearestTimer(eventLoop);
if (shortest) {
long now_sec, now_ms;
aeGetTime(&now_sec, &now_ms);
tvp = &tv;
tvp->tv_sec = shortest->when_sec - now_sec;
if (shortest->when_ms < now_ms) {
tvp->tv_usec = ((shortest->when_ms + 1000) - now_ms) * 1000;
tvp->tv_sec--;
} else {
tvp->tv_usec = (shortest->when_ms - now_ms) * 1000;
}
if (tvp->tv_sec < 0) tvp->tv_sec = 0;
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
} else {
if (flags & AE_DONT_WAIT) {
tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
} else {
tvp = NULL;
}
}
numevents = aeApiPoll(eventLoop, tvp);
for (j = 0; j < numevents; j++) {
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = 0;
if (fe->mask & mask & AE_READABLE) {
rfired = 1;
fe->rfileProc(eventLoop, fd, fe->clientData, mask);
}
if (fe->mask & mask & AE_WRITABLE) {
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop, fd, fe->clientData, mask);
}
processed++;
}
}
if (flags & AE_TIME_EVENTS)
processed += processTimeEvents(eventLoop);
return processed;
}
int catAeWait(int fd, int mask, long long milliseconds) {
struct pollfd pfd;
int retmask = 0, retval;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fd;
if (mask & AE_READABLE) {
pfd.events |= POLLIN;
}
if (mask & AE_WRITABLE) {
pfd.events |= POLLOUT;
}
if (mask & AE_ERROR) {
pfd.events |= POLLERR;
}
if (mask & AE_HUP) {
pfd.events |= POLLHUP;
}
#ifdef WIN32
if ((retval = WSAPoll(&pfd, 1, milliseconds)) == 1)
#else
if ((retval = poll(&pfd, 1, milliseconds)) == 1)
#endif {
if (pfd.revents & POLLIN) {
retmask |= AE_READABLE;
}
if (pfd.revents & POLLOUT) {
retmask |= AE_WRITABLE;
}
if (pfd.revents & POLLERR) {
retmask |= AE_ERROR;
}
if (pfd.revents & POLLHUP) {
retmask |= AE_HUP;
}
return retmask;
} else {
return retval;
}
}
void catAeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
catAeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
char *catAeGetApiName(void) {
return aeApiName();
}
void catAeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep) {
eventLoop->beforesleep = beforesleep;
}