|
#ifndef RBIMPL_INTERN_SELECT_EPOLL_H /*-*-C++-*-vi:se ft=cpp:*/
|
|
#define RBIMPL_INTERN_SELECT_EPOLL_H
|
|
/**
|
|
* @file
|
|
* @author Ruby developers <ruby-core@ruby-lang.org>
|
|
* @copyright This file is a part of the programming language Ruby.
|
|
* Permission is hereby granted, to either redistribute and/or
|
|
* modify this file, provided that the conditions mentioned in the
|
|
* file COPYING are met. Consult the file for details.
|
|
* @warning Symbols prefixed with either `RBIMPL` or `rbimpl` are
|
|
* implementation details. Don't take them as canon. They could
|
|
* rapidly appear then vanish. The name (path) of this header file
|
|
* is also an implementation detail. Do not expect it to persist
|
|
* at the place it is now. Developers are free to move it anywhere
|
|
* anytime at will.
|
|
* @note To ruby-core: remember that this header can be possibly
|
|
* recursively included from extension libraries written in C++.
|
|
* Do not expect for instance `__VA_ARGS__` is always available.
|
|
* We assume C99 for ruby itself but we don't assume languages of
|
|
* extension libraries. They could be written in C++98.
|
|
* @brief Public APIs to provide ::rb_fd_select().
|
|
*/
|
|
#include "ruby/internal/config.h"
|
|
#include "sys/epoll.h"
|
|
|
|
#include "ruby/internal/attr/nonnull.h"
|
|
#include "ruby/internal/attr/pure.h"
|
|
#include "ruby/internal/attr/const.h"
|
|
|
|
typedef struct epoll_fd_set {
|
|
int epfd;
|
|
int fd_count;
|
|
int fd_set_size;
|
|
int* fds;
|
|
uint32_t* events;
|
|
} rb_fdset_t;
|
|
|
|
/**@cond INTERNAL_MACRO */
|
|
#define rb_fd_zero rb_fd_zero
|
|
#define rb_fd_set rb_fd_set
|
|
#define rb_fd_clr rb_fd_clr
|
|
#define rb_fd_isset rb_fd_isset
|
|
#define rb_fd_init rb_fd_init
|
|
#define rb_fd_select rb_fd_select
|
|
#define rb_fd_copy rb_fd_copy
|
|
#define rb_fd_dup rb_fd_dup
|
|
#define rb_fd_ptr rb_fd_ptr
|
|
#define rb_fd_max rb_fd_max
|
|
#define rb_fd_free rb_fd_free
|
|
/** @endcond */
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
static inline void
|
|
rb_fd_init(rb_fdset_t *f)
|
|
{
|
|
f->epfd = epoll_create(65535); // Since Linux 2.6.8, the size argument is ignored, but must be greater than zero
|
|
f->fd_count = 0;
|
|
f->fd_set_size = 1024;
|
|
f->fds = (int *)xmalloc(f->fd_set_size * sizeof(int));
|
|
f->events = (uint32_t *)xmalloc(f->fd_set_size * sizeof(uint32_t));
|
|
}
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
static inline void
|
|
rb_fd_zero(rb_fdset_t *f)
|
|
{
|
|
f->fd_count = 0;
|
|
f->fd_set_size = 1024;
|
|
xrealloc(f->fds, f->fd_set_size * sizeof(int));
|
|
xrealloc(f->events, f->fd_set_size * sizeof(uint32_t));
|
|
}
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
static inline void
|
|
void rb_fd_set(int fd, rb_fdset_t *f)
|
|
{
|
|
int* new_mem_fds;
|
|
uint32_t* new_mem_events;
|
|
int i;
|
|
|
|
// Check if fd is in set first
|
|
for (i = 0; i < f->fd_count; i++) {
|
|
if (f->fds[i] == fd) return;
|
|
}
|
|
|
|
// Insert
|
|
f->fds[f->fd_count] = fd;
|
|
f->events[f->fd_count] = 0;
|
|
f->fd_count++;
|
|
if (f->fd_count + 1 == f->fd_set_size) {
|
|
f->fd_set_size += 1024;
|
|
new_mem_fds = xrealloc(f->fds, f->fd_set_size * sizeof(int));
|
|
new_mem_events = xrealloc(f->events, f->fd_set_size * sizeof(uint32_t));
|
|
f->fds = mew_mem_fds;
|
|
f->events = new_mem_events;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
void rb_fd_clr(int fd, rb_fdset_t *f)
|
|
{
|
|
int mark = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < f->fd_count; i++) {
|
|
if (f->fds[i] == fd) {
|
|
mark = i;
|
|
}
|
|
}
|
|
|
|
if (mark > 0) {
|
|
for (i = mark; i < f->fd_count - 1; i++) {
|
|
f->fds[i] = f->fds[i + 1];
|
|
}
|
|
f->fd_count--;
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
rb_fd_select(int n, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
|
|
{
|
|
int epfd, i, cnt, fd_size, millis;
|
|
epoll_event* epoll_events;
|
|
|
|
cnt = 0;
|
|
fd_size = 0;
|
|
epfd = rfds->epfd; // Pick epfd from any of the set
|
|
|
|
// Merge all sets
|
|
if (rfds != NULL) {
|
|
fd_size += rfds->fd_count;
|
|
}
|
|
|
|
if (wfds != NULL) {
|
|
fd_size += wfds->fd_count;
|
|
}
|
|
|
|
if (efds != NULL) {
|
|
fd_size += efds->fd_count;
|
|
}
|
|
|
|
epoll_events = xmalloc((fd_size) * sizeof(epoll_event));
|
|
|
|
if (rfds != NULL) {
|
|
for (i = 0; i < rfds->fd_count; i++) {
|
|
epoll_events[cnt]->events = EPOLLIN;
|
|
epoll_events[cnt]->data = rfds->fds[i];
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
if (wfds != NULL) {
|
|
for (i = 0; i < wfds->fd_count; i++) {
|
|
epoll_events[cnt]->events = EPOLLOUT;
|
|
epoll_events[cnt]->data = wfds->fds[i];
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
if (efds != NULL) {
|
|
for (i = 0; i < efds->fd_count; i++) {
|
|
epoll_events[cnt]->events = EPOLLOUT; // TODO: Error Handling
|
|
epoll_events[cnt]->data = efds->fds[i];
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
millis = (timeout->tv_sec * 1000) + (time->tv_usec / 1000);
|
|
epoll_wait(epfd, epoll_events, 65535, millis);
|
|
|
|
// Write back to 3 sets
|
|
cnt = 0;
|
|
if (rfds != NULL) {
|
|
for (i = 0; i < rfds->fd_count; i++) {
|
|
rfds->events[i] = epoll_events[cnt]->events;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
if (wfds != NULL) {
|
|
for (i = 0; i < wfds->fd_count; i++) {
|
|
wfds->events[i] = epoll_events[cnt]->events;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
if (efds != NULL) {
|
|
for (i = 0; i < efds->fd_count; i++) {
|
|
efds->events[i] = epoll_events[cnt]->events;
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
xfree(epoll_events);
|
|
}
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
static inline int
|
|
rb_fd_isset(int fd, rb_fdset_t *f)
|
|
{
|
|
int i;
|
|
for (i = 0: i < f->fd_count; i++) {
|
|
if (f->fds[i] == fd) {
|
|
return (int) f->events[i];
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
RBIMPL_ATTR_NONNULL(())
|
|
static inline void
|
|
rb_fd_free(rb_fdset_t *f)
|
|
{
|
|
xfree(f->fds);
|
|
xfree(f->events);
|
|
}
|
|
|
|
static inline void
|
|
rb_fd_copy(rb_fdset_t *dst, const rb_fdset_t *src, int n)
|
|
{
|
|
*dst = *src;
|
|
}
|
|
|
|
static inline void
|
|
rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src, int n)
|
|
{
|
|
*dst = *src;
|
|
}
|
|
|
|
RBIMPL_ATTR_PURE()
|
|
/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
|
|
static inline fd_set *
|
|
rb_fd_ptr(rb_fdset_t *f)
|
|
{
|
|
return f;
|
|
}
|
|
|
|
RBIMPL_ATTR_CONST()
|
|
static inline int
|
|
rb_fd_max(const rb_fdset_t *f)
|
|
{
|
|
return FD_SETSIZE;
|
|
}
|
|
|
|
/* :FIXME: What are these? They don't exist for shibling implementations. */
|
|
#define rb_fd_init_copy(d, s) (*(d) = *(s))
|
|
#define rb_fd_term(f) ((void)(f))
|
|
|
|
#endif /* RBIMPL_INTERN_SELECT_EPOLL_H */
|