Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

379 lines (302 sloc) 8.938 kB
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2011 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "common-internal.h"
#include "fdpoll-protected.h"
#include "util.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#define KQUEUE_READ_EVENT 1
#define KQUEUE_WRITE_EVENT (1 << 1)
#define KQUEUE_CLOSE (1 << 2)
/***********************************************************************/
/* kqueue, kevent: kernel event notification mechanism */
/* */
/* #include <sys/event.h> */
/* #include <sys/time.h> */
/* */
/* int */
/* kqueue(void); */
/* */
/* int */
/* kevent(int kq, const struct kevent *changelist, size_t nchanges, */
/* struct kevent *eventlist, size_t nevents, */
/* const struct timespec *timeout); */
/* */
/* EV_SET(&kev, ident, filter, flags, fflags, data, udata); */
/* */
/***********************************************************************/
typedef struct {
struct cherokee_fdpoll poll;
int kqueue;
struct kevent *changelist;
int *fdevents;
int *fdinterest;
size_t nchanges;
} cherokee_fdpoll_kqueue_t;
static ret_t
_free (cherokee_fdpoll_kqueue_t *fdp)
{
if (fdp == NULL)
return ret_ok;
if (fdp->kqueue >= 0)
close (fdp->kqueue);
/* ANSI C required, so that free() can handle NULL pointers
*/
free (fdp->changelist);
free (fdp->fdevents);
free (fdp->fdinterest);
/* Caller has to set this pointer to NULL.
*/
free (fdp);
return ret_ok;
}
static ret_t
_add_change (cherokee_fdpoll_kqueue_t *fdp, int fd, int mode, int change)
{
int index;
struct kevent *event;
/* The 'mode' variable should contain a single mode here. This
* function does not support multiple modes.
*/
index = fdp->nchanges;
if (unlikely (index >= FDPOLL(fdp)->nfiles)) {
PRINT_ERROR_S("kqueue_add: fdpoll is full !\n");
return ret_error;
}
event = &fdp->changelist[index];
event->ident = fd;
switch (mode) {
case poll_mode_read:
event->filter = EVFILT_READ;
break;
case poll_mode_write:
event->filter = EVFILT_WRITE;
break;
default:
event->filter = 0;
SHOULDNT_HAPPEN;
}
event->flags = change;
event->fflags = 0;
fdp->fdinterest[fd] = mode;
fdp->nchanges++;
return ret_ok;
}
static ret_t
_add (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw_mode)
{
ret_t ret = ret_not_found;
if (rw_mode & poll_mode_read) {
ret = _add_change (fdp, fd, poll_mode_read, EV_ADD);
if (ret != ret_ok)
return ret;
}
if (rw_mode & poll_mode_write) {
ret = _add_change (fdp, fd, poll_mode_write, EV_ADD);
if (ret != ret_ok)
return ret;
}
if (ret == ret_ok) {
FDPOLL(fdp)->npollfds++;
}
return ret;
}
static ret_t
_del (cherokee_fdpoll_kqueue_t *fdp, int fd)
{
ret_t ret = ret_not_found;
int interest = fdp->fdinterest[fd];
cherokee_boolean_t error = false;
if (interest & poll_mode_read) {
ret = _add_change (fdp, fd, poll_mode_read, EV_DELETE);
if (ret != ret_ok)
error = true;
}
if (interest & poll_mode_write) {
ret = _add_change (fdp, fd, poll_mode_write, EV_DELETE);
if (ret != ret_ok)
error |= true;
}
if ((ret == ret_ok) && (! error)) {
FDPOLL(fdp)->npollfds--;
}
fdp->fdinterest[fd] = 0;
return ret;
}
static int
_watch (cherokee_fdpoll_kqueue_t *fdp, int timeout_msecs)
{
struct timespec timeout;
int i;
int n_events;
timeout.tv_sec = timeout_msecs/1000L;
timeout.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L;
/* Get the events of the file descriptors with
* activity
*/
again:
n_events = kevent (fdp->kqueue,
fdp->changelist,
fdp->nchanges,
fdp->changelist,
FDPOLL(fdp)->nfiles,
&timeout);
fdp->nchanges = 0;
if (unlikely (n_events < 0)) {
if (errno == EINTR)
goto again;
LOG_ERRNO (errno, cherokee_err_error, CHEROKEE_ERROR_FDPOLL_KQUEUE);
return 0;
} else if (n_events >= 0) {
memset (fdp->fdevents, 0, FDPOLL(fdp)->system_nfiles * sizeof(int));
for (i = 0; i < n_events; ++i) {
/* Filter */
if (fdp->changelist[i].filter == EVFILT_READ) {
fdp->fdevents[fdp->changelist[i].ident] = KQUEUE_READ_EVENT;
} else if (fdp->changelist[i].filter == EVFILT_WRITE) {
fdp->fdevents[fdp->changelist[i].ident] = KQUEUE_WRITE_EVENT;
} else {
SHOULDNT_HAPPEN;
}
/* Flags */
if (fdp->changelist[i].flags & (EV_EOF | EV_ERROR)) {
fdp->fdevents[fdp->changelist[i].ident] = KQUEUE_CLOSE;
}
}
}
return n_events;
}
static int
_check (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw_mode)
{
uint32_t events;
/* Sanity check: is it a wrong fd?
*/
if (unlikely (fd < 0)) {
return -1;
}
events = fdp->fdevents[fd];
if (events & KQUEUE_CLOSE) {
return 1;
}
if ((rw_mode & poll_mode_read) &&
(events & KQUEUE_READ_EVENT))
{
return 1;
}
if ((rw_mode & poll_mode_write) &&
(events & KQUEUE_WRITE_EVENT))
{
return 1;
}
return events;
}
static ret_t
_reset (cherokee_fdpoll_kqueue_t *fdp, int fd)
{
UNUSED(fdp);
UNUSED(fd);
return ret_ok;
}
static ret_t
_set_mode (cherokee_fdpoll_kqueue_t *fdp, int fd, int rw_mode)
{
int prev_interest = fdp->fdinterest[fd];
/* If transitioning from R->W or from W->R disable any active
* event on the fd as we are no longer interested on it.
*/
/* No longer reading */
if ((! (rw_mode & poll_mode_read)) &&
(prev_interest & poll_mode_read))
{
_add_change (fdp, fd, poll_mode_read, EV_DELETE);
}
/* No longer writing */
if ((! (rw_mode & poll_mode_write)) &&
(prev_interest & poll_mode_write))
{
_add_change (fdp, fd, poll_mode_write, EV_DELETE);
}
return _add_change (fdp, fd, rw_mode, EV_ADD);
}
ret_t
fdpoll_kqueue_get_fdlimits (cuint_t *system_fd_limit, cuint_t *fd_limit)
{
*system_fd_limit = 0;
*fd_limit = 0;
return ret_ok;
}
ret_t
fdpoll_kqueue_new (cherokee_fdpoll_t **fdp, int sys_fd_limit, int fd_limit)
{
int re;
cherokee_fdpoll_t *nfd;
CHEROKEE_CNEW_STRUCT (1, n, fdpoll_kqueue);
nfd = FDPOLL(n);
/* Init base class properties
*/
nfd->type = cherokee_poll_kqueue;
nfd->nfiles = fd_limit;
nfd->system_nfiles = sys_fd_limit;
nfd->npollfds = 0;
/* Init base class virtual methods
*/
nfd->free = (fdpoll_func_free_t) _free;
nfd->add = (fdpoll_func_add_t) _add;
nfd->del = (fdpoll_func_del_t) _del;
nfd->reset = (fdpoll_func_reset_t) _reset;
nfd->set_mode = (fdpoll_func_set_mode_t) _set_mode;
nfd->check = (fdpoll_func_check_t) _check;
nfd->watch = (fdpoll_func_watch_t) _watch;
/* Init kqueue specific variables
*/
n->kqueue = -1;
n->nchanges = 0;
n->changelist = (struct kevent *) calloc (nfd->nfiles * 2, sizeof(struct kevent));
n->fdevents = (int *) calloc (nfd->system_nfiles, sizeof(int));
n->fdinterest = (int *) calloc (nfd->system_nfiles, sizeof(int));
if (n->fdevents == NULL || n->changelist == NULL || n->fdinterest == NULL) {
_free (n);
return ret_nomem;
}
n->kqueue = kqueue();
if (n->kqueue == -1) {
_free (n);
return ret_error;
}
re = fcntl (n->kqueue, F_SETFD, FD_CLOEXEC);
if (re < 0) {
LOG_ERRNO (errno, cherokee_err_error,
CHEROKEE_ERROR_FDPOLL_EPOLL_CLOEXEC);
_free (n);
return ret_error;
}
/* Return the object
*/
*fdp = nfd;
return ret_ok;
}
Jump to Line
Something went wrong with that request. Please try again.