Merge new files from the trunk

git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/branches/stable@564 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2012-10-28 00:51:28 +00:00
parent eb1f64505b
commit 01da712896
4 changed files with 647 additions and 0 deletions

385
src/linux/platform.c Normal file
View File

@ -0,0 +1,385 @@
/*
* Copyright (c) 2011 Mark Heily <mark@heily.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../common/private.h"
//XXX-FIXME TEMP
const struct filter evfilt_proc = EVFILT_NOTIMPL;
/*
* Per-thread epoll event buffer used to ferry data between
* kevent_wait() and kevent_copyout().
*/
static __thread struct epoll_event epevt[MAX_KEVENT];
const struct kqueue_vtable kqops = {
linux_kqueue_init,
linux_kqueue_free,
linux_kevent_wait,
linux_kevent_copyout,
NULL,
NULL,
linux_eventfd_init,
linux_eventfd_close,
linux_eventfd_raise,
linux_eventfd_lower,
linux_eventfd_descriptor
};
// NOT USED YET: taken from trunk
#ifdef TODO
int
linux_kqueue_init(struct kqueue *kq)
{
kq->kq_id = epoll_create(1);
if (kq->kq_id < 0) {
dbg_perror("epoll_create(2)");
return (-1);
}
if (filter_register_all(kq) < 0) {
close(kq->kq_id);
return (-1);
}
#if DEADWOOD
//might be useful in posix
/* Add each filter's pollable descriptor to the epollset */
for (i = 0; i < EVFILT_SYSCOUNT; i++) {
filt = &kq->kq_filt[i];
if (filt->kf_id == 0)
continue;
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN;
ev.data.ptr = filt;
if (epoll_ctl(kq->kq_id, EPOLL_CTL_ADD, filt->kf_pfd, &ev) < 0) {
dbg_perror("epoll_ctl(2)");
close(kq->kq_id);
return (-1);
}
}
#endif
return (0);
}
void
linux_kqueue_free(struct kqueue *kq UNUSED)
{
abort();//FIXME
}
static int
linux_kevent_wait_hires(
struct kqueue *kq,
const struct timespec *timeout)
{
fd_set fds;
int n;
int epfd;
dbg_printf("waiting for events (timeout=%ld sec %ld nsec)",
timeout->tv_sec, timeout->tv_nsec);
epfd = kqueue_epfd(kq);
FD_ZERO(&fds);
FD_SET(epfd, &fds);
n = pselect(epfd + 1, &fds, NULL , NULL, timeout, NULL);
if (n < 0) {
if (errno == EINTR) {
dbg_puts("signal caught");
return (-1);
}
dbg_perror("pselect(2)");
return (-1);
}
return (n);
}
int
linux_kevent_wait(
struct kqueue *kq,
int nevents,
const struct timespec *ts)
{
int timeout, nret;
/* Use pselect() if the timeout value is less than one millisecond. */
if (ts != NULL && ts->tv_sec == 0 && ts->tv_nsec < 1000000) {
nret = linux_kevent_wait_hires(kq, ts);
/* Otherwise, use epoll_wait() directly */
} else {
/* Convert timeout to the format used by epoll_wait() */
if (ts == NULL)
timeout = -1;
else
timeout = (1000 * ts->tv_sec) + (ts->tv_nsec / 1000000);
dbg_puts("waiting for events");
nret = epoll_wait(kqueue_epfd(kq), &epevt[0], nevents, timeout);
if (nret < 0) {
dbg_perror("epoll_wait");
return (-1);
}
}
return (nret);
}
int
linux_kevent_copyout(struct kqueue *kq, int nready,
struct kevent *eventlist, int nevents UNUSED)
{
struct epoll_event *ev;
struct filter *filt;
struct knote *kn;
int i, nret, rv;
nret = nready;
for (i = 0; i < nready; i++) {
ev = &epevt[i];
kn = (struct knote *) ev->data.ptr;
knote_lock(kn);
filt = &kq->kq_filt[~(kn->kev.filter)];
rv = filt->kf_copyout(eventlist, kn, ev);
if (slowpath(rv < 0)) {
dbg_puts("knote_copyout failed");
/* XXX-FIXME: hard to handle this without losing events */
abort();
}
/*
* Certain flags cause the associated knote to be deleted
* or disabled.
*/
if (eventlist->flags & EV_DISPATCH)
knote_disable(filt, kn); //FIXME: Error checking
if (eventlist->flags & EV_ONESHOT) {
knote_delete(filt, kn); //FIXME: Error checking
} else {
knote_unlock(kn);
}
/* If an empty kevent structure is returned, the event is discarded. */
/* TODO: add these semantics to windows + solaris platform.c */
if (fastpath(eventlist->filter != 0)) {
eventlist++;
} else {
dbg_puts("spurious wakeup, discarding event");
nret--;
}
}
return (nret);
}
#endif //NOT USED
int
linux_eventfd_init(struct eventfd *e)
{
int evfd;
if ((evfd = eventfd(0, 0)) < 0) {
dbg_perror("eventfd");
return (-1);
}
if (fcntl(evfd, F_SETFL, O_NONBLOCK) < 0) {
dbg_perror("fcntl");
close(evfd);
return (-1);
}
e->ef_id = evfd;
return (0);
}
void
linux_eventfd_close(struct eventfd *e)
{
close(e->ef_id);
e->ef_id = -1;
}
int
linux_eventfd_raise(struct eventfd *e)
{
uint64_t counter;
int rv = 0;
dbg_puts("raising event level");
counter = 1;
if (write(e->ef_id, &counter, sizeof(counter)) < 0) {
switch (errno) {
case EAGAIN:
/* Not considered an error */
break;
case EINTR:
rv = -EINTR;
break;
default:
dbg_printf("write(2): %s", strerror(errno));
rv = -1;
}
}
return (rv);
}
int
linux_eventfd_lower(struct eventfd *e)
{
uint64_t cur;
ssize_t n;
int rv = 0;
/* Reset the counter */
dbg_puts("lowering event level");
n = read(e->ef_id, &cur, sizeof(cur));
if (n < 0) {
switch (errno) {
case EAGAIN:
/* Not considered an error */
break;
case EINTR:
rv = -EINTR;
break;
default:
dbg_printf("read(2): %s", strerror(errno));
rv = -1;
}
} else if (n != sizeof(cur)) {
dbg_puts("short read");
rv = -1;
}
return (rv);
}
int
linux_eventfd_descriptor(struct eventfd *e)
{
return (e->ef_id);
}
int
linux_get_descriptor_type(struct knote *kn)
{
socklen_t slen;
struct stat sb;
int i, lsock;
/*
* Test if the descriptor is a socket.
*/
if (fstat(kn->kev.ident, &sb) < 0) {
dbg_perror("fstat(2)");
return (-1);
}
if (! S_ISSOCK(sb.st_mode)) {
//FIXME: could be a pipe, device file, or other non-regular file
kn->kn_flags |= KNFL_REGULAR_FILE;
dbg_printf("fd %d is a regular file\n", (int)kn->kev.ident);
return (0);
}
/*
* Test if the socket is active or passive.
*/
slen = sizeof(lsock);
lsock = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, (char *) &lsock, &slen);
if (i < 0) {
switch (errno) {
case ENOTSOCK: /* same as lsock = 0 */
return (0);
break;
default:
dbg_perror("getsockopt(3)");
return (-1);
}
} else {
if (lsock)
kn->kn_flags |= KNFL_PASSIVE_SOCKET;
return (0);
}
}
char *
epoll_event_dump(struct epoll_event *evt)
{
static __thread char buf[128];
if (evt == NULL)
return "(null)";
#define EPEVT_DUMP(attrib) \
if (evt->events & attrib) \
strcat(&buf[0], #attrib" ");
snprintf(&buf[0], 128, " { data = %p, events = ", evt->data.ptr);
EPEVT_DUMP(EPOLLIN);
EPEVT_DUMP(EPOLLOUT);
#if defined(HAVE_EPOLLRDHUP)
EPEVT_DUMP(EPOLLRDHUP);
#endif
EPEVT_DUMP(EPOLLONESHOT);
EPEVT_DUMP(EPOLLET);
strcat(&buf[0], "}\n");
return (&buf[0]);
#undef EPEVT_DUMP
}
int
epoll_update(int op, struct filter *filt, struct knote *kn, struct epoll_event *ev)
{
dbg_printf("op=%d fd=%d events=%s", op, (int)kn->kev.ident,
epoll_event_dump(ev));
if (epoll_ctl(filter_epfd(filt), op, kn->kev.ident, ev) < 0) {
dbg_printf("epoll_ctl(2): %s", strerror(errno));
return (-1);
}
return (0);
}
/*
* Given a file descriptor, return the path to the file it refers to.
*/
int
linux_fd_to_path(char *buf, size_t bufsz, int fd)
{
char path[1024]; //TODO: Maxpathlen, etc.
if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
return (-1);
memset(buf, 0, bufsz);
return (readlink(path, buf, bufsz));
}

86
src/linux/platform.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2011 Mark Heily <mark@heily.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _KQUEUE_LINUX_PLATFORM_H
#define _KQUEUE_LINUX_PLATFORM_H
struct filter;
#include <sys/epoll.h>
#include <sys/queue.h>
#include <sys/inotify.h>
#include <sys/eventfd.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
/*
* Get the current thread ID
*/
# define _GNU_SOURCE
# include <linux/unistd.h>
# include <sys/syscall.h>
# include <unistd.h>
extern long int syscall (long int __sysno, ...);
/* Convenience macros to access the epoll descriptor for the kqueue */
#define kqueue_epfd(kq) ((kq)->kq_id)
#define filter_epfd(filt) ((filt)->kf_kqueue->kq_id)
/*
* Additional members of struct filter
*/
#undef FILTER_PLATFORM_SPECIFIC
/*
* Additional members of struct knote
*/
#define KNOTE_PLATFORM_SPECIFIC \
int kn_epollfd; /* A copy of filter->epfd */ \
union { \
int kn_timerfd; \
int kn_signalfd; \
int kn_inotifyfd; \
int kn_eventfd; \
} kdata
/*
* Additional members of struct kqueue
*/
#define KQUEUE_PLATFORM_SPECIFIC \
struct epoll_event kq_plist[MAX_KEVENT]; \
size_t kq_nplist
int linux_kqueue_init(struct kqueue *);
void linux_kqueue_free(struct kqueue *);
int linux_kevent_wait(struct kqueue *, int, const struct timespec *);
int linux_kevent_copyout(struct kqueue *, int, struct kevent *, int);
int linux_knote_copyout(struct kevent *, struct knote *);
int linux_eventfd_init(struct eventfd *);
void linux_eventfd_close(struct eventfd *);
int linux_eventfd_raise(struct eventfd *);
int linux_eventfd_lower(struct eventfd *);
int linux_eventfd_descriptor(struct eventfd *);
/* utility functions */
int linux_get_descriptor_type(struct knote *);
int linux_fd_to_path(char *, size_t, int);
#endif /* ! _KQUEUE_LINUX_PLATFORM_H */

90
src/posix/platform.c Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2011 Mark Heily <mark@heily.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../common/private.h"
int
posix_kqueue_init(struct kqueue *kq UNUSED)
{
return (0);
}
void
posix_kqueue_free(struct kqueue *kq UNUSED)
{
}
int
posix_eventfd_init(struct eventfd *e)
{
int sd[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sd) < 0) {
return (-1);
}
if ((fcntl(sd[0], F_SETFL, O_NONBLOCK) < 0) ||
(fcntl(sd[1], F_SETFL, O_NONBLOCK) < 0)) {
close(sd[0]);
close(sd[1]);
return (-1);
}
e->ef_wfd = sd[0];
e->ef_id = sd[1];
return (0);
}
void
posix_eventfd_close(struct eventfd *e)
{
close(e->ef_id);
close(e->ef_wfd);
e->ef_id = -1;
}
int
posix_eventfd_raise(struct eventfd *e)
{
dbg_puts("raising event level");
if (write(e->ef_wfd, ".", 1) < 0) {
/* FIXME: handle EAGAIN and EINTR */
dbg_printf("write(2) on fd %d: %s", e->ef_wfd, strerror(errno));
return (-1);
}
return (0);
}
int
posix_eventfd_lower(struct eventfd *e)
{
char buf[1024];
/* Reset the counter */
dbg_puts("lowering event level");
if (read(e->ef_id, &buf, sizeof(buf)) < 0) {
/* FIXME: handle EAGAIN and EINTR */
/* FIXME: loop so as to consume all data.. may need mutex */
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
return (0);
}
int
posix_eventfd_descriptor(struct eventfd *e)
{
return (e->ef_id);
}

86
src/posix/platform.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2011 Mark Heily <mark@heily.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _KQUEUE_POSIX_PLATFORM_H
#define _KQUEUE_POSIX_PLATFORM_H
/* Required by glibc for MAP_ANON */
#define __USE_MISC 1
#include "../../include/sys/event.h"
/*
* GCC-compatible atomic operations
*/
#define atomic_inc(p) __sync_add_and_fetch((p), 1)
#define atomic_dec(p) __sync_sub_and_fetch((p), 1)
#define atomic_cas(p, oval, nval) __sync_val_compare_and_swap(p, oval, nval)
#define atomic_ptr_cas(p, oval, nval) __sync_val_compare_and_swap(p, oval, nval)
/*
* GCC-compatible branch prediction macros
*/
#define fastpath(x) __builtin_expect((x), 1)
#define slowpath(x) __builtin_expect((x), 0)
/*
* GCC-compatible attributes
*/
#ifdef MAKE_STATIC
# define CONSTRUCTOR
#else
# define CONSTRUCTOR __attribute__ ((constructor))
#endif
#define VISIBLE __attribute__((visibility("default")))
#define HIDDEN __attribute__((visibility("hidden")))
#define UNUSED __attribute__((unused))
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
#include <poll.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
/*
* Additional members of 'struct eventfd'
*/
#define EVENTFD_PLATFORM_SPECIFIC \
int ef_wfd
void posix_kqueue_free(struct kqueue *);
int posix_kqueue_init(struct kqueue *);
int posix_kevent_wait(struct kqueue *, const struct timespec *);
int posix_kevent_copyout(struct kqueue *, int, struct kevent *, int);
int posix_eventfd_init(struct eventfd *);
void posix_eventfd_close(struct eventfd *);
int posix_eventfd_raise(struct eventfd *);
int posix_eventfd_lower(struct eventfd *);
int posix_eventfd_descriptor(struct eventfd *);
#endif /* ! _KQUEUE_POSIX_PLATFORM_H */