#include "sys_defs.h"
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <limits.h>
#ifdef USE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "mymalloc.h"
#include "msg.h"
#include "iostuff.h"
#include "ring.h"
#include "events.h"
#if !defined(EVENTS_STYLE)
#error "must define EVENTS_STYLE"
#endif
#define EVENT_ALLOC_INCR 10
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
typedef fd_set EVENT_MASK;
#define EVENT_MASK_BYTE_COUNT(mask) sizeof(*(mask))
#define EVENT_MASK_ZERO(mask) FD_ZERO(mask)
#define EVENT_MASK_SET(fd, mask) FD_SET((fd), (mask))
#define EVENT_MASK_ISSET(fd, mask) FD_ISSET((fd), (mask))
#define EVENT_MASK_CLR(fd, mask) FD_CLR((fd), (mask))
#define EVENT_MASK_CMP(m1, m2) memcmp((m1), (m2), EVENT_MASK_BYTE_COUNT(m1))
#else
typedef struct {
char *data;
size_t data_len;
} EVENT_MASK;
#define EVENT_MASK_NBBY (8)
#define EVENT_MASK_FD_BYTE(fd, mask) \
(((unsigned char *) (mask)->data)[(fd) / EVENT_MASK_NBBY])
#define EVENT_MASK_FD_BIT(fd) (1 << ((fd) % EVENT_MASK_NBBY))
#define EVENT_MASK_BYTES_NEEDED(len) \
(((len) + (EVENT_MASK_NBBY -1)) / EVENT_MASK_NBBY)
#define EVENT_MASK_BYTE_COUNT(mask) ((mask)->data_len)
#define EVENT_MASK_ALLOC(mask, bit_len) do { \
size_t _byte_len = EVENT_MASK_BYTES_NEEDED(bit_len); \
(mask)->data = mymalloc(_byte_len); \
memset((mask)->data, 0, _byte_len); \
(mask)->data_len = _byte_len; \
} while (0)
#define EVENT_MASK_REALLOC(mask, bit_len) do { \
size_t _byte_len = EVENT_MASK_BYTES_NEEDED(bit_len); \
size_t _old_len = (mask)->data_len; \
(mask)->data = myrealloc((mask)->data, _byte_len); \
if (_byte_len > _old_len) \
memset((mask)->data + _old_len, 0, _byte_len - _old_len); \
(mask)->data_len = _byte_len; \
} while (0)
#define EVENT_MASK_FREE(mask) myfree((mask)->data)
#define EVENT_MASK_ZERO(mask) \
memset((mask)->data, 0, (mask)->data_len)
#define EVENT_MASK_SET(fd, mask) \
(EVENT_MASK_FD_BYTE((fd), (mask)) |= EVENT_MASK_FD_BIT(fd))
#define EVENT_MASK_ISSET(fd, mask) \
(EVENT_MASK_FD_BYTE((fd), (mask)) & EVENT_MASK_FD_BIT(fd))
#define EVENT_MASK_CLR(fd, mask) \
(EVENT_MASK_FD_BYTE((fd), (mask)) &= ~EVENT_MASK_FD_BIT(fd))
#define EVENT_MASK_CMP(m1, m2) \
memcmp((m1)->data, (m2)->data, EVENT_MASK_BYTE_COUNT(m1))
#endif
typedef struct EVENT_FDTABLE EVENT_FDTABLE;
struct EVENT_FDTABLE {
EVENT_NOTIFY_RDWR_FN callback;
char *context;
};
static EVENT_MASK event_rmask;
static EVENT_MASK event_wmask;
static EVENT_MASK event_xmask;
static int event_fdlimit;
static EVENT_FDTABLE *event_fdtable;
static int event_fdslots;
static int event_max_fd = -1;
#if (EVENTS_STYLE == EVENTS_STYLE_KQUEUE)
#include <sys/event.h>
#ifndef EV_SET
#define EV_SET(kp, id, fi, fl, ffl, da, ud) do { \
(kp)->ident = (id); \
(kp)->filter = (fi); \
(kp)->flags = (fl); \
(kp)->fflags = (ffl); \
(kp)->data = (da); \
(kp)->udata = (ud); \
} while(0)
#endif
static int event_kq;
#define EVENT_REG_INIT_HANDLE(er, n) do { \
er = event_kq = kqueue(); \
} while (0)
#define EVENT_REG_INIT_TEXT "kqueue"
#define EVENT_REG_FORK_HANDLE(er, n) do { \
(void) close(event_kq); \
EVENT_REG_INIT_HANDLE(er, (n)); \
} while (0)
#define EVENT_REG_FD_OP(er, fh, ev, op) do { \
struct kevent dummy; \
EV_SET(&dummy, (fh), (ev), (op), 0, 0, 0); \
(er) = kevent(event_kq, &dummy, 1, 0, 0, 0); \
} while (0)
#define EVENT_REG_ADD_OP(e, f, ev) EVENT_REG_FD_OP((e), (f), (ev), EV_ADD)
#define EVENT_REG_ADD_READ(e, f) EVENT_REG_ADD_OP((e), (f), EVFILT_READ)
#define EVENT_REG_ADD_WRITE(e, f) EVENT_REG_ADD_OP((e), (f), EVFILT_WRITE)
#define EVENT_REG_ADD_TEXT "kevent EV_ADD"
#define EVENT_REG_DEL_OP(e, f, ev) EVENT_REG_FD_OP((e), (f), (ev), EV_DELETE)
#define EVENT_REG_DEL_READ(e, f) EVENT_REG_DEL_OP((e), (f), EVFILT_READ)
#define EVENT_REG_DEL_WRITE(e, f) EVENT_REG_DEL_OP((e), (f), EVFILT_WRITE)
#define EVENT_REG_DEL_TEXT "kevent EV_DELETE"
typedef struct kevent EVENT_BUFFER;
#define EVENT_BUFFER_READ(event_count, event_buf, buflen, delay) do { \
struct timespec ts; \
struct timespec *tsp; \
if ((delay) < 0) { \
tsp = 0; \
} else { \
tsp = &ts; \
ts.tv_nsec = 0; \
ts.tv_sec = (delay); \
} \
(event_count) = kevent(event_kq, (struct kevent *) 0, 0, (event_buf), \
(buflen), (tsp)); \
} while (0)
#define EVENT_BUFFER_READ_TEXT "kevent"
#define EVENT_GET_FD(bp) ((bp)->ident)
#define EVENT_GET_TYPE(bp) ((bp)->filter)
#define EVENT_TEST_READ(bp) (EVENT_GET_TYPE(bp) == EVFILT_READ)
#define EVENT_TEST_WRITE(bp) (EVENT_GET_TYPE(bp) == EVFILT_WRITE)
#endif
#if (EVENTS_STYLE == EVENTS_STYLE_DEVPOLL)
#include <sys/devpoll.h>
#include <fcntl.h>
static int event_pollfd;
#define EVENT_REG_INIT_HANDLE(er, n) do { \
er = event_pollfd = open("/dev/poll", O_RDWR); \
if (event_pollfd >= 0) close_on_exec(event_pollfd, CLOSE_ON_EXEC); \
} while (0)
#define EVENT_REG_INIT_TEXT "open /dev/poll"
#define EVENT_REG_FORK_HANDLE(er, n) do { \
(void) close(event_pollfd); \
EVENT_REG_INIT_HANDLE(er, (n)); \
} while (0)
#define EVENT_REG_FD_OP(er, fh, ev) do { \
struct pollfd dummy; \
dummy.fd = (fh); \
dummy.events = (ev); \
(er) = write(event_pollfd, (void *) &dummy, \
sizeof(dummy)) != sizeof(dummy) ? -1 : 0; \
} while (0)
#define EVENT_REG_ADD_READ(e, f) EVENT_REG_FD_OP((e), (f), POLLIN)
#define EVENT_REG_ADD_WRITE(e, f) EVENT_REG_FD_OP((e), (f), POLLOUT)
#define EVENT_REG_ADD_TEXT "write /dev/poll"
#define EVENT_REG_DEL_BOTH(e, f) EVENT_REG_FD_OP((e), (f), POLLREMOVE)
#define EVENT_REG_DEL_TEXT "write /dev/poll"
typedef struct pollfd EVENT_BUFFER;
#define EVENT_BUFFER_READ(event_count, event_buf, buflen, delay) do { \
struct dvpoll dvpoll; \
dvpoll.dp_fds = (event_buf); \
dvpoll.dp_nfds = (buflen); \
dvpoll.dp_timeout = (delay) < 0 ? -1 : (delay) * 1000; \
(event_count) = ioctl(event_pollfd, DP_POLL, &dvpoll); \
} while (0)
#define EVENT_BUFFER_READ_TEXT "ioctl DP_POLL"
#define EVENT_GET_FD(bp) ((bp)->fd)
#define EVENT_GET_TYPE(bp) ((bp)->revents)
#define EVENT_TEST_READ(bp) (EVENT_GET_TYPE(bp) & POLLIN)
#define EVENT_TEST_WRITE(bp) (EVENT_GET_TYPE(bp) & POLLOUT)
#endif
#if (EVENTS_STYLE == EVENTS_STYLE_EPOLL)
#include <sys/epoll.h>
static int event_epollfd;
#define EVENT_REG_INIT_HANDLE(er, n) do { \
er = event_epollfd = epoll_create(n); \
if (event_epollfd >= 0) close_on_exec(event_epollfd, CLOSE_ON_EXEC); \
} while (0)
#define EVENT_REG_INIT_TEXT "epoll_create"
#define EVENT_REG_FORK_HANDLE(er, n) do { \
(void) close(event_epollfd); \
EVENT_REG_INIT_HANDLE(er, (n)); \
} while (0)
#define EVENT_REG_FD_OP(er, fh, ev, op) do { \
struct epoll_event dummy; \
dummy.events = (ev); \
dummy.data.fd = (fh); \
(er) = epoll_ctl(event_epollfd, (op), (fh), &dummy); \
} while (0)
#define EVENT_REG_ADD_OP(e, f, ev) EVENT_REG_FD_OP((e), (f), (ev), EPOLL_CTL_ADD)
#define EVENT_REG_ADD_READ(e, f) EVENT_REG_ADD_OP((e), (f), EPOLLIN)
#define EVENT_REG_ADD_WRITE(e, f) EVENT_REG_ADD_OP((e), (f), EPOLLOUT)
#define EVENT_REG_ADD_TEXT "epoll_ctl EPOLL_CTL_ADD"
#define EVENT_REG_DEL_OP(e, f, ev) EVENT_REG_FD_OP((e), (f), (ev), EPOLL_CTL_DEL)
#define EVENT_REG_DEL_READ(e, f) EVENT_REG_DEL_OP((e), (f), EPOLLIN)
#define EVENT_REG_DEL_WRITE(e, f) EVENT_REG_DEL_OP((e), (f), EPOLLOUT)
#define EVENT_REG_DEL_TEXT "epoll_ctl EPOLL_CTL_DEL"
typedef struct epoll_event EVENT_BUFFER;
#define EVENT_BUFFER_READ(event_count, event_buf, buflen, delay) do { \
(event_count) = epoll_wait(event_epollfd, (event_buf), (buflen), \
(delay) < 0 ? -1 : (delay) * 1000); \
} while (0)
#define EVENT_BUFFER_READ_TEXT "epoll_wait"
#define EVENT_GET_FD(bp) ((bp)->data.fd)
#define EVENT_GET_TYPE(bp) ((bp)->events)
#define EVENT_TEST_READ(bp) (EVENT_GET_TYPE(bp) & EPOLLIN)
#define EVENT_TEST_WRITE(bp) (EVENT_GET_TYPE(bp) & EPOLLOUT)
#endif
typedef struct EVENT_TIMER EVENT_TIMER;
struct EVENT_TIMER {
time_t when;
EVENT_NOTIFY_TIME_FN callback;
char *context;
long loop_instance;
RING ring;
};
static RING event_timer_head;
static long event_loop_instance;
#define RING_TO_TIMER(r) \
((EVENT_TIMER *) ((void *) (r) - offsetof(EVENT_TIMER, ring)))
#define FOREACH_QUEUE_ENTRY(entry, head) \
for (entry = ring_succ(head); entry != (head); entry = ring_succ(entry))
#define FIRST_TIMER(head) \
(ring_succ(head) != (head) ? RING_TO_TIMER(ring_succ(head)) : 0)
static time_t event_present;
#define EVENT_INIT_NEEDED() (event_present == 0)
static void event_init(void)
{
EVENT_FDTABLE *fdp;
int err;
if (!EVENT_INIT_NEEDED())
msg_panic("event_init: repeated call");
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
if ((event_fdlimit = open_limit(FD_SETSIZE)) < 0)
msg_fatal("unable to determine open file limit");
#else
if ((event_fdlimit = open_limit(INT_MAX)) < 0)
msg_fatal("unable to determine open file limit");
#endif
if (event_fdlimit < FD_SETSIZE / 2 && event_fdlimit < 256)
msg_warn("could allocate space for only %d open files", event_fdlimit);
event_fdslots = EVENT_ALLOC_INCR;
event_fdtable = (EVENT_FDTABLE *)
mymalloc(sizeof(EVENT_FDTABLE) * event_fdslots);
for (fdp = event_fdtable; fdp < event_fdtable + event_fdslots; fdp++) {
fdp->callback = 0;
fdp->context = 0;
}
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
EVENT_MASK_ZERO(&event_rmask);
EVENT_MASK_ZERO(&event_wmask);
EVENT_MASK_ZERO(&event_xmask);
#else
EVENT_MASK_ALLOC(&event_rmask, event_fdslots);
EVENT_MASK_ALLOC(&event_wmask, event_fdslots);
EVENT_MASK_ALLOC(&event_xmask, event_fdslots);
EVENT_REG_INIT_HANDLE(err, event_fdslots);
if (err < 0)
msg_fatal("%s: %m", EVENT_REG_INIT_TEXT);
#endif
ring_init(&event_timer_head);
(void) time(&event_present);
if (EVENT_INIT_NEEDED())
msg_panic("event_init: unable to initialize");
}
static void event_extend(int fd)
{
const char *myname = "event_extend";
int old_slots = event_fdslots;
int new_slots = (event_fdslots > fd / 2 ?
2 * old_slots : fd + EVENT_ALLOC_INCR);
EVENT_FDTABLE *fdp;
#ifdef EVENT_REG_UPD_HANDLE
int err;
#endif
if (msg_verbose > 2)
msg_info("%s: fd %d", myname, fd);
event_fdtable = (EVENT_FDTABLE *)
myrealloc((void *) event_fdtable, sizeof(EVENT_FDTABLE) * new_slots);
event_fdslots = new_slots;
for (fdp = event_fdtable + old_slots;
fdp < event_fdtable + new_slots; fdp++) {
fdp->callback = 0;
fdp->context = 0;
}
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
EVENT_MASK_REALLOC(&event_rmask, new_slots);
EVENT_MASK_REALLOC(&event_wmask, new_slots);
EVENT_MASK_REALLOC(&event_xmask, new_slots);
#endif
#ifdef EVENT_REG_UPD_HANDLE
EVENT_REG_UPD_HANDLE(err, new_slots);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_UPD_TEXT);
#endif
}
time_t event_time(void)
{
if (EVENT_INIT_NEEDED())
event_init();
return (event_present);
}
void event_drain(int time_limit)
{
EVENT_MASK zero_mask;
time_t max_time;
if (EVENT_INIT_NEEDED())
return;
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
EVENT_MASK_ZERO(&zero_mask);
#else
EVENT_MASK_ALLOC(&zero_mask, event_fdslots);
#endif
(void) time(&event_present);
max_time = event_present + time_limit;
while (event_present < max_time
&& (event_timer_head.pred != &event_timer_head
|| EVENT_MASK_CMP(&zero_mask, &event_xmask) != 0)) {
event_loop(1);
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
if (EVENT_MASK_BYTE_COUNT(&zero_mask)
!= EVENT_MASK_BYTES_NEEDED(event_fdslots))
EVENT_MASK_REALLOC(&zero_mask, event_fdslots);
#endif
}
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
EVENT_MASK_FREE(&zero_mask);
#endif
}
void event_fork(void)
{
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
EVENT_FDTABLE *fdp;
int err;
int fd;
if (EVENT_INIT_NEEDED())
return;
EVENT_REG_FORK_HANDLE(err, event_fdslots);
if (err < 0)
msg_fatal("%s: %m", EVENT_REG_INIT_TEXT);
for (fd = 0; fd <= event_max_fd; fd++) {
if (EVENT_MASK_ISSET(fd, &event_wmask)) {
EVENT_MASK_CLR(fd, &event_wmask);
fdp = event_fdtable + fd;
event_enable_write(fd, fdp->callback, fdp->context);
} else if (EVENT_MASK_ISSET(fd, &event_rmask)) {
EVENT_MASK_CLR(fd, &event_rmask);
fdp = event_fdtable + fd;
event_enable_read(fd, fdp->callback, fdp->context);
}
}
#endif
}
void event_enable_read(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
{
const char *myname = "event_enable_read";
EVENT_FDTABLE *fdp;
int err;
if (EVENT_INIT_NEEDED())
event_init();
if (fd < 0 || fd >= event_fdlimit)
msg_panic("%s: bad file descriptor: %d", myname, fd);
if (msg_verbose > 2)
msg_info("%s: fd %d", myname, fd);
if (fd >= event_fdslots)
event_extend(fd);
if (EVENT_MASK_ISSET(fd, &event_wmask))
msg_panic("%s: fd %d: read/write I/O request", myname, fd);
if (EVENT_MASK_ISSET(fd, &event_rmask) == 0) {
EVENT_MASK_SET(fd, &event_xmask);
EVENT_MASK_SET(fd, &event_rmask);
if (event_max_fd < fd)
event_max_fd = fd;
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
EVENT_REG_ADD_READ(err, fd);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_ADD_TEXT);
#endif
}
fdp = event_fdtable + fd;
if (fdp->callback != callback || fdp->context != context) {
fdp->callback = callback;
fdp->context = context;
}
}
void event_enable_write(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
{
const char *myname = "event_enable_write";
EVENT_FDTABLE *fdp;
int err;
if (EVENT_INIT_NEEDED())
event_init();
if (fd < 0 || fd >= event_fdlimit)
msg_panic("%s: bad file descriptor: %d", myname, fd);
if (msg_verbose > 2)
msg_info("%s: fd %d", myname, fd);
if (fd >= event_fdslots)
event_extend(fd);
if (EVENT_MASK_ISSET(fd, &event_rmask))
msg_panic("%s: fd %d: read/write I/O request", myname, fd);
if (EVENT_MASK_ISSET(fd, &event_wmask) == 0) {
EVENT_MASK_SET(fd, &event_xmask);
EVENT_MASK_SET(fd, &event_wmask);
if (event_max_fd < fd)
event_max_fd = fd;
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
EVENT_REG_ADD_WRITE(err, fd);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_ADD_TEXT);
#endif
}
fdp = event_fdtable + fd;
if (fdp->callback != callback || fdp->context != context) {
fdp->callback = callback;
fdp->context = context;
}
}
void event_disable_readwrite(int fd)
{
const char *myname = "event_disable_readwrite";
EVENT_FDTABLE *fdp;
int err;
if (EVENT_INIT_NEEDED())
event_init();
if (fd < 0 || fd >= event_fdlimit)
msg_panic("%s: bad file descriptor: %d", myname, fd);
if (msg_verbose > 2)
msg_info("%s: fd %d", myname, fd);
if (fd >= event_fdslots)
return;
#if (EVENTS_STYLE != EVENTS_STYLE_SELECT)
#ifdef EVENT_REG_DEL_BOTH
if (EVENT_MASK_ISSET(fd, &event_rmask)
|| EVENT_MASK_ISSET(fd, &event_wmask)) {
EVENT_REG_DEL_BOTH(err, fd);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_DEL_TEXT);
}
#else
if (EVENT_MASK_ISSET(fd, &event_rmask)) {
EVENT_REG_DEL_READ(err, fd);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_DEL_TEXT);
} else if (EVENT_MASK_ISSET(fd, &event_wmask)) {
EVENT_REG_DEL_WRITE(err, fd);
if (err < 0)
msg_fatal("%s: %s: %m", myname, EVENT_REG_DEL_TEXT);
}
#endif
#endif
EVENT_MASK_CLR(fd, &event_xmask);
EVENT_MASK_CLR(fd, &event_rmask);
EVENT_MASK_CLR(fd, &event_wmask);
fdp = event_fdtable + fd;
fdp->callback = 0;
fdp->context = 0;
}
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
{
const char *myname = "event_request_timer";
RING *ring;
EVENT_TIMER *timer;
if (EVENT_INIT_NEEDED())
event_init();
if (delay < 0)
msg_panic("%s: invalid delay: %d", myname, delay);
time(&event_present);
FOREACH_QUEUE_ENTRY(ring, &event_timer_head) {
timer = RING_TO_TIMER(ring);
if (timer->callback == callback && timer->context == context) {
timer->when = event_present + delay;
timer->loop_instance = event_loop_instance;
ring_detach(ring);
if (msg_verbose > 2)
msg_info("%s: reset 0x%lx 0x%lx %d", myname,
(long) callback, (long) context, delay);
break;
}
}
if (ring == &event_timer_head) {
timer = (EVENT_TIMER *) mymalloc(sizeof(EVENT_TIMER));
timer->when = event_present + delay;
timer->callback = callback;
timer->context = context;
timer->loop_instance = event_loop_instance;
if (msg_verbose > 2)
msg_info("%s: set 0x%lx 0x%lx %d", myname,
(long) callback, (long) context, delay);
}
FOREACH_QUEUE_ENTRY(ring, &event_timer_head) {
if (timer->when < RING_TO_TIMER(ring)->when)
break;
}
ring_prepend(ring, &timer->ring);
return (timer->when);
}
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
{
const char *myname = "event_cancel_timer";
RING *ring;
EVENT_TIMER *timer;
int time_left = -1;
if (EVENT_INIT_NEEDED())
event_init();
FOREACH_QUEUE_ENTRY(ring, &event_timer_head) {
timer = RING_TO_TIMER(ring);
if (timer->callback == callback && timer->context == context) {
if ((time_left = timer->when - event_present) < 0)
time_left = 0;
ring_detach(ring);
myfree((void *) timer);
break;
}
}
if (msg_verbose > 2)
msg_info("%s: 0x%lx 0x%lx %d", myname,
(long) callback, (long) context, time_left);
return (time_left);
}
void event_loop(int delay)
{
const char *myname = "event_loop";
static int nested;
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
fd_set rmask;
fd_set wmask;
fd_set xmask;
struct timeval tv;
struct timeval *tvp;
int new_max_fd;
#else
EVENT_BUFFER event_buf[100];
EVENT_BUFFER *bp;
#endif
int event_count;
EVENT_TIMER *timer;
int fd;
EVENT_FDTABLE *fdp;
int select_delay;
if (EVENT_INIT_NEEDED())
event_init();
if (msg_verbose > 2) {
RING *ring;
FOREACH_QUEUE_ENTRY(ring, &event_timer_head) {
timer = RING_TO_TIMER(ring);
msg_info("%s: time left %3d for 0x%lx 0x%lx", myname,
(int) (timer->when - event_present),
(long) timer->callback, (long) timer->context);
}
}
if ((timer = FIRST_TIMER(&event_timer_head)) != 0) {
event_present = time((time_t *) 0);
if ((select_delay = timer->when - event_present) < 0) {
select_delay = 0;
} else if (delay >= 0 && select_delay > delay) {
select_delay = delay;
}
} else {
select_delay = delay;
}
if (msg_verbose > 2)
msg_info("event_loop: select_delay %d", select_delay);
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
if (select_delay < 0) {
tvp = 0;
} else {
tvp = &tv;
tv.tv_usec = 0;
tv.tv_sec = select_delay;
}
rmask = event_rmask;
wmask = event_wmask;
xmask = event_xmask;
event_count = select(event_max_fd + 1, &rmask, &wmask, &xmask, tvp);
if (event_count < 0) {
if (errno != EINTR)
msg_fatal("event_loop: select: %m");
return;
}
#else
EVENT_BUFFER_READ(event_count, event_buf,
sizeof(event_buf) / sizeof(event_buf[0]),
select_delay);
if (event_count < 0) {
if (errno != EINTR)
msg_fatal("event_loop: " EVENT_BUFFER_READ_TEXT ": %m");
return;
}
#endif
if (nested++ > 0)
msg_panic("event_loop: recursive call");
event_present = time((time_t *) 0);
event_loop_instance += 1;
while ((timer = FIRST_TIMER(&event_timer_head)) != 0) {
if (timer->when > event_present)
break;
if (timer->loop_instance == event_loop_instance)
break;
ring_detach(&timer->ring);
if (msg_verbose > 2)
msg_info("%s: timer 0x%lx 0x%lx", myname,
(long) timer->callback, (long) timer->context);
timer->callback(EVENT_TIME, timer->context);
myfree((void *) timer);
}
#if (EVENTS_STYLE == EVENTS_STYLE_SELECT)
if (event_count > 0) {
for (new_max_fd = 0, fd = 0; fd <= event_max_fd; fd++) {
if (FD_ISSET(fd, &event_xmask)) {
new_max_fd = fd;
fdp = event_fdtable + fd;
if (FD_ISSET(fd, &xmask)) {
if (msg_verbose > 2)
msg_info("%s: exception fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback, (long) fdp->context);
fdp->callback(EVENT_XCPT, fdp->context);
} else if (FD_ISSET(fd, &wmask)) {
if (msg_verbose > 2)
msg_info("%s: write fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback, (long) fdp->context);
fdp->callback(EVENT_WRITE, fdp->context);
} else if (FD_ISSET(fd, &rmask)) {
if (msg_verbose > 2)
msg_info("%s: read fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback, (long) fdp->context);
fdp->callback(EVENT_READ, fdp->context);
}
}
}
event_max_fd = new_max_fd;
}
#else
for (bp = event_buf; bp < event_buf + event_count; bp++) {
fd = EVENT_GET_FD(bp);
if (fd < 0 || fd > event_max_fd)
msg_panic("%s: bad file descriptor: %d", myname, fd);
if (EVENT_MASK_ISSET(fd, &event_xmask)) {
fdp = event_fdtable + fd;
if (EVENT_TEST_READ(bp)) {
if (msg_verbose > 2)
msg_info("%s: read fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback, (long) fdp->context);
fdp->callback(EVENT_READ, fdp->context);
} else if (EVENT_TEST_WRITE(bp)) {
if (msg_verbose > 2)
msg_info("%s: write fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback,
(long) fdp->context);
fdp->callback(EVENT_WRITE, fdp->context);
} else {
if (msg_verbose > 2)
msg_info("%s: other fd=%d act=0x%lx 0x%lx", myname,
fd, (long) fdp->callback, (long) fdp->context);
fdp->callback(EVENT_XCPT, fdp->context);
}
}
}
#endif
nested--;
}
#ifdef TEST
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
static void timer_event(int unused_event, void *context)
{
printf("%ld: %s\n", (long) event_present, context);
fflush(stdout);
}
static void echo(int unused_event, void *unused_context)
{
char buf[BUFSIZ];
if (fgets(buf, sizeof(buf), stdin) == 0)
exit(0);
printf("Result: %s", buf);
}
static void request(int unused_event, void *unused_context)
{
event_request_timer(timer_event, "3 first", 3);
event_request_timer(timer_event, "3 second", 3);
event_request_timer(timer_event, "4 first", 4);
event_request_timer(timer_event, "4 second", 4);
event_request_timer(timer_event, "2 first", 2);
event_request_timer(timer_event, "2 second", 2);
event_request_timer(timer_event, "1 first", 1);
event_request_timer(timer_event, "1 second", 1);
event_request_timer(timer_event, "0 first", 0);
event_request_timer(timer_event, "0 second", 0);
}
int main(int argc, void **argv)
{
if (argv[1])
msg_verbose = atoi(argv[1]);
event_request_timer(request, (void *) 0, 0);
event_enable_read(fileno(stdin), echo, (void *) 0);
event_drain(10);
exit(0);
}
#endif