170 lines
4.4 KiB
C
170 lines
4.4 KiB
C
#include "iowatcher.h"
|
|
|
|
#ifdef EVENT_SELECT
|
|
#include "hplatform.h"
|
|
#include "hdef.h"
|
|
#include "hevent.h"
|
|
#include "hsocket.h"
|
|
|
|
typedef struct select_ctx_s {
|
|
int max_fd;
|
|
fd_set readfds;
|
|
fd_set writefds;
|
|
int nread;
|
|
int nwrite;
|
|
} select_ctx_t;
|
|
|
|
int iowatcher_init(hloop_t* loop) {
|
|
if (loop->iowatcher) return 0;
|
|
select_ctx_t* select_ctx;
|
|
HV_ALLOC_SIZEOF(select_ctx);
|
|
select_ctx->max_fd = -1;
|
|
FD_ZERO(&select_ctx->readfds);
|
|
FD_ZERO(&select_ctx->writefds);
|
|
select_ctx->nread = 0;
|
|
select_ctx->nwrite = 0;
|
|
loop->iowatcher = select_ctx;
|
|
return 0;
|
|
}
|
|
|
|
int iowatcher_cleanup(hloop_t* loop) {
|
|
HV_FREE(loop->iowatcher);
|
|
return 0;
|
|
}
|
|
|
|
int iowatcher_add_event(hloop_t* loop, int fd, int events) {
|
|
if (loop->iowatcher == NULL) {
|
|
iowatcher_init(loop);
|
|
}
|
|
select_ctx_t* select_ctx = (select_ctx_t*)loop->iowatcher;
|
|
if (fd > select_ctx->max_fd) {
|
|
select_ctx->max_fd = fd;
|
|
}
|
|
if (events & HV_READ) {
|
|
if (!FD_ISSET(fd, &select_ctx->readfds)) {
|
|
FD_SET(fd, &select_ctx->readfds);
|
|
select_ctx->nread++;
|
|
}
|
|
}
|
|
if (events & HV_WRITE) {
|
|
if (!FD_ISSET(fd, &select_ctx->writefds)) {
|
|
FD_SET(fd, &select_ctx->writefds);
|
|
select_ctx->nwrite++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int iowatcher_del_event(hloop_t* loop, int fd, int events) {
|
|
select_ctx_t* select_ctx = (select_ctx_t*)loop->iowatcher;
|
|
if (select_ctx == NULL) return 0;
|
|
if (fd == select_ctx->max_fd) {
|
|
select_ctx->max_fd = -1;
|
|
}
|
|
if (events & HV_READ) {
|
|
if (FD_ISSET(fd, &select_ctx->readfds)) {
|
|
FD_CLR(fd, &select_ctx->readfds);
|
|
select_ctx->nread--;
|
|
}
|
|
}
|
|
if (events & HV_WRITE) {
|
|
if (FD_ISSET(fd, &select_ctx->writefds)) {
|
|
FD_CLR(fd, &select_ctx->writefds);
|
|
select_ctx->nwrite--;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int find_max_active_fd(hloop_t* loop) {
|
|
hio_t* io = NULL;
|
|
for (int i = loop->ios.maxsize-1; i >= 0; --i) {
|
|
io = loop->ios.ptr[i];
|
|
if (io && io->active && io->events) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int remove_bad_fds(hloop_t* loop) {
|
|
select_ctx_t* select_ctx = (select_ctx_t*)loop->iowatcher;
|
|
if (select_ctx == NULL) return 0;
|
|
int badfds = 0;
|
|
int error = 0;
|
|
socklen_t optlen = sizeof(error);
|
|
for (int fd = 0; fd <= select_ctx->max_fd; ++fd) {
|
|
if (FD_ISSET(fd, &select_ctx->readfds) ||
|
|
FD_ISSET(fd, &select_ctx->writefds)) {
|
|
error = 0;
|
|
optlen = sizeof(int);
|
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &optlen) < 0 || error != 0) {
|
|
++badfds;
|
|
hio_t* io = loop->ios.ptr[fd];
|
|
if (io) {
|
|
hio_del(io, HV_RDWR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return badfds;
|
|
}
|
|
|
|
int iowatcher_poll_events(hloop_t* loop, int timeout) {
|
|
select_ctx_t* select_ctx = (select_ctx_t*)loop->iowatcher;
|
|
if (select_ctx == NULL) return 0;
|
|
if (select_ctx->nread == 0 && select_ctx->nwrite == 0) {
|
|
return 0;
|
|
}
|
|
int max_fd = select_ctx->max_fd;
|
|
fd_set readfds = select_ctx->readfds;
|
|
fd_set writefds = select_ctx->writefds;
|
|
if (max_fd == -1) {
|
|
select_ctx->max_fd = max_fd = find_max_active_fd(loop);
|
|
}
|
|
struct timeval tv, *tp;
|
|
if (timeout == INFINITE) {
|
|
tp = NULL;
|
|
}
|
|
else {
|
|
tv.tv_sec = timeout / 1000;
|
|
tv.tv_usec = (timeout % 1000) * 1000;
|
|
tp = &tv;
|
|
}
|
|
int nselect = select(max_fd+1, &readfds, &writefds, NULL, tp);
|
|
if (nselect < 0) {
|
|
#ifdef OS_WIN
|
|
if (WSAGetLastError() == WSAENOTSOCK) {
|
|
#else
|
|
if (errno == EBADF) {
|
|
perror("select");
|
|
#endif
|
|
remove_bad_fds(loop);
|
|
return -EBADF;
|
|
}
|
|
return nselect;
|
|
}
|
|
if (nselect == 0) return 0;
|
|
int nevents = 0;
|
|
int revents = 0;
|
|
for (int fd = 0; fd <= max_fd; ++fd) {
|
|
revents = 0;
|
|
if (FD_ISSET(fd, &readfds)) {
|
|
++nevents;
|
|
revents |= HV_READ;
|
|
}
|
|
if (FD_ISSET(fd, &writefds)) {
|
|
++nevents;
|
|
revents |= HV_WRITE;
|
|
}
|
|
if (revents) {
|
|
hio_t* io = loop->ios.ptr[fd];
|
|
if (io) {
|
|
io->revents = revents;
|
|
EVENT_PENDING(io);
|
|
}
|
|
}
|
|
if (nevents == nselect) break;
|
|
}
|
|
return nevents;
|
|
}
|
|
#endif
|