This commit is contained in:
Laurence Withers 2007-10-13 23:14:47 +00:00
parent 938852afd3
commit 60b9d9cc48
8 changed files with 443 additions and 21 deletions

View File

@ -9,6 +9,8 @@
#define HEADER_liblwevent #define HEADER_liblwevent
/* standard includes, or includes needed for type declarations */ /* standard includes, or includes needed for type declarations */
#include <signal.h> /* for 400_signalfd.h */
#include <stdint.h> /* for 400_signalfd.h */
/* options for text editors /* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4; kate: replace-trailing-space-save true; space-indent true; tab-width 4;

View File

@ -13,6 +13,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/syscall.h> /* for 400_signalfd.c */
/* options for text editors /* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4; kate: replace-trailing-space-save true; space-indent true; tab-width 4;

View File

@ -8,17 +8,18 @@
/* _lwevent_activate() /* _lwevent_activate()
* Activates the event `ev'. Called when creating a new event in lwevent_new() or when reactivating * Activates the event `ev'. Called with EPOLL_CTL_ADD in `op' when creating a new event in
* an event in lwevent_reactivate(). Calls epoll_ctl(EPOLL_CTL_ADD). * lwevent_new() or when reactivating an event in lwevent_reactivate(). Called with EPOLL_CTL_MOD
* in lwevent_set_events(). Calls epoll_ctl(), returning -1 on permanent failure.
*/ */
static int _lwevent_activate(struct lwevent* ev) static int _lwevent_activate(int op, struct lwevent* ev)
{ {
struct epoll_event ee; struct epoll_event ee;
ee.events = ev->events; ee.events = ev->events;
ee.data.ptr = ev; ee.data.ptr = ev;
return TEMP_FAILURE_RETRY( epoll_ctl(lwevent_epoll_fd, EPOLL_CTL_ADD, ev->fd, &ee) ); return TEMP_FAILURE_RETRY( epoll_ctl(lwevent_epoll_fd, op, ev->fd, &ee) );
} }
@ -38,7 +39,7 @@ struct lwevent* lwevent_new(int fd, int events, lwevent_callback callback)
ev->events = events; ev->events = events;
ev->callback = callback; ev->callback = callback;
if(_lwevent_activate(ev)) { if(_lwevent_activate(EPOLL_CTL_ADD, ev)) {
free(ev); free(ev);
return 0; return 0;
} }
@ -79,7 +80,7 @@ int lwevent_deactivate(struct lwevent* ev)
*/ */
int lwevent_reactivate(struct lwevent* ev) int lwevent_reactivate(struct lwevent* ev)
{ {
return _lwevent_activate(ev); return _lwevent_activate(EPOLL_CTL_ADD, ev);
} }

View File

@ -28,14 +28,13 @@ retrieve associated values.
*/ */
struct lwevent struct lwevent
#ifndef DOXYGEN #ifdef DOXYGEN
;
#else
{ {
/*! \brief Dummy variable for doxygen */ /*! \brief Dummy variable for doxygen */
int dummy; int dummy;
}; }
#endif #endif
;

View File

@ -0,0 +1,93 @@
/* liblwevent/src/liblwevent/300_fdparams.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
/* lwevent_get_*()
* Functions for retrieving attributes stored in a struct lwevent.
*/
int lwevent_get_fd(const struct lwevent* ev)
{
return ev->fd;
}
int lwevent_get_events(const struct lwevent* ev)
{
return ev->events;
}
lwevent_callback lwevent_get_callback(const struct lwevent* ev)
{
return ev->callback;
}
void* lwevent_get_user(const struct lwevent* ev)
{
return ev->user;
}
lwevent_dtor lwevent_get_dtor(const struct lwevent* ev)
{
return ev->dtor;
}
int lwevent_get_autodestroy(const struct lwevent* ev)
{
return ev->autodestroy;
}
/* lwevent_set_events()
* Calls epoll_ctl(EPOLL_CTL_MOD) to change the events associated with the object `ev'. Does not
* perform the call if the new events match the current events. If `old_events' is not null, stores
* the existing events before changing. Returns -1 if epoll_ctl fails.
*/
int lwevent_set_events(struct lwevent* ev, int events, int* old_events)
{
if(old_events) *old_events = ev->events;
if(events == ev->events) return 0; /* don't call epoll_ctl() if no change */
return _lwevent_activate(EPOLL_CTL_MOD, ev);
}
/* lwevent_set_*()
* Changes attributes associated with `ev', possibly storing the old value.
*/
void lwevent_set_callback(struct lwevent* ev, lwevent_callback callback,
lwevent_callback* old_callback)
{
if(old_callback) *old_callback = ev->callback;
ev->callback = callback;
}
void lwevent_set_user(struct lwevent* ev, void* user, void** old_user)
{
if(old_user) *old_user = ev->user;
ev->user = user;
}
void lwevent_set_dtor(struct lwevent* ev, lwevent_dtor dtor, lwevent_dtor* old_dtor)
{
if(old_dtor) *old_dtor = ev->dtor;
ev->dtor = dtor;
}
void lwevent_set_autodestroy(struct lwevent* ev, int autodestroy, int* old_autodestroy)
{
if(old_autodestroy) *old_autodestroy = ev->autodestroy;
ev->autodestroy = autodestroy;
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4
*/

View File

@ -1,4 +1,4 @@
/* liblwevent/src/liblwevent/300_fdevent.h /* liblwevent/src/liblwevent/300_fdparams.h
* *
* (c)2007, Laurence Withers, <l@lwithers.me.uk>. * (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or * Released under the GNU GPLv2. See file COPYING or
@ -29,15 +29,6 @@ typedef void (*lwevent_dtor)(struct lwevent* ev);
/*! \brief Flag: autodestroy object if \c EPOLLERR is received. */
extern const int LWEVENT_AUTODESTROY_ERR;
/*! \brief Flag: autodestroy object if \c EPOLLRDHUP is received. */
extern const int LWEVENT_AUTODESTROY_RDHUP;
/*! \brief Flag: autodestroy object if \c EPOLLHUP is received. */
extern const int LWEVENT_AUTODESTROY_WRHUP;
/*! \brief Retrieve file descriptor associated with object. */ /*! \brief Retrieve file descriptor associated with object. */
int lwevent_get_fd(const struct lwevent* ev) __attribute__((nonnull, warn_unused_result)); int lwevent_get_fd(const struct lwevent* ev) __attribute__((nonnull, warn_unused_result));
@ -145,7 +136,9 @@ void lwevent_set_dtor(struct lwevent* ev, lwevent_dtor dtor, lwevent_dtor* old_d
\param[out] old_autodestroy Previous bitmask stored here. May be 0. \param[out] old_autodestroy Previous bitmask stored here. May be 0.
The autodestroy functionality causes the event object \a ev to be automatically passed to The autodestroy functionality causes the event object \a ev to be automatically passed to
lwevent_free() if any specified event occurs. You may pass ~0 to autodestroy on any error event. lwevent_free() if any specified event occurs. You may pass ~0 to autodestroy on any event. See
\c epoll_ctl(2) for a list of valid event flags (non-authoritative list: \c EPOLLIN, \c EPOLLOUT,
\c EPOLLRDHUP, \c EPOLLPRI, \c EPOLLERR, \c EPOLLHUP).
*/ */
void lwevent_set_autodestroy(struct lwevent* ev, int autodestroy, int* old_autodestroy) void lwevent_set_autodestroy(struct lwevent* ev, int autodestroy, int* old_autodestroy)

View File

@ -0,0 +1,145 @@
/* liblwevent/src/liblwevent/400_signalfd.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
int signalfd(int fd, const sigset_t* mask)
{
size_t sz;
sz = sizeof(sigset_t);
return syscall(__NR_signalfd, fd, mask, sz);
}
struct lwevent_signal {
sigset_t mask;
lwevent_signalcb callback;
struct lwevent* ev;
};
static void _lwevent_signalfd_callback(struct lwevent* ev, int revents)
{
(void)revents;
struct signalfd_siginfo ssi;
struct lwevent_signal* si;
si = lwevent_get_user(ev);
while(1) {
if(TEMP_FAILURE_RETRY( read(lwevent_get_fd(ev), &ssi, sizeof(ssi)) ) == -1) {
if(errno == EAGAIN) return;
/* TODO: now what??? */
}
si->callback(&ssi);
}
}
struct lwevent_signal* lwevent_signalfd(const sigset_t* mask, lwevent_signalcb callback)
{
struct lwevent_signal* si;
int fd;
si = malloc(sizeof(struct lwevent_signal));
if(!si) return 0;
memset(si, 0, sizeof(struct lwevent_signal));
si->mask = *mask;
sigdelset(&si->mask, SIGBUS);
sigdelset(&si->mask, SIGFPE);
sigdelset(&si->mask, SIGILL);
sigdelset(&si->mask, SIGSEGV);
si->callback = callback;
fd = signalfd(-1, &si->mask);
if(fd == -1) {
free(si);
return 0;
}
/* TODO: make non-blocking */
si->ev = lwevent_new(fd, EPOLLIN, _lwevent_signalfd_callback);
if(!si->ev) {
close(fd);
free(si);
return 0;
}
lwevent_set_user(si->ev, si, 0);
return si;
}
void lwevent_signalfd_getsigs(const struct lwevent_signal* si, sigset_t* mask)
{
*mask = si->mask;
}
lwevent_signalcb lwevent_signalfd_getcb(const struct lwevent_signal* si)
{
return si->callback;
}
int lwevent_signalfd_setsigs(struct lwevent_signal* si, const sigset_t* mask, sigset_t* old_mask)
{
sigset_t mnew;
if(old_mask) *old_mask = si->mask;
mnew = *mask;
sigdelset(&mnew, SIGBUS);
sigdelset(&mnew, SIGFPE);
sigdelset(&mnew, SIGILL);
sigdelset(&mnew, SIGSEGV);
if(signalfd(lwevent_get_fd(si->ev), &mnew)) return -1;
si->mask = mnew;
return 0;
}
void lwevent_signalfd_setcb(struct lwevent_signal* si, lwevent_signalcb callback,
lwevent_signalcb* old_callback)
{
if(old_callback) *old_callback = si->callback;
si->callback = callback;
}
void lwevent_signalfd_close(struct lwevent_signal* si)
{
if(!si) return;
lwevent_free(si->ev);
free(si);
}
struct lwevent_signal* lwevent_signalfd_default(void)
{
/* TODO */
return lwevent_signalfd(...);
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4
*/

View File

@ -0,0 +1,188 @@
/* liblwevent/src/liblwevent/400_signalfd.h
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv2. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
/*! \defgroup signalfd Signals through file descriptor
This module provides an interface to \c signalfd(2), a system call which provides an alternative
signal delivery mechanism: certain signals can be read from a file descriptor queue.
\todo Currently, the \c signalfd(2) interface is very new (in fact, the manpage has not yet been
released and must be pieced together from http://lkml.org/lkml/2007/9/27/88). Furthermore, it
is not yet implemented in \a glibc. Until that occurs, a function call wrapper for the system
call is provided in this library. Eventually this will be removed in favour of the authoritative
\a glibc interface.
*/
/*!@{*/
/*! \brief Signal info structure.
This is the structure that is returned from a \c read(2) of a signalfd.
\todo Eventually, this will be folded into glibc. At that time, this structure definition will have
to be removed and the code updated to use the new stuff.
*/
struct signalfd_siginfo {
uint32_t ssi_signo; /*!< si_signo */
int32_t ssi_err; /*!< si_errno */
int32_t ssi_code; /*!< si_code */
uint32_t ssi_pid; /*!< si_pid */
uint32_t ssi_uid; /*!< si_uid */
int32_t ssi_fd; /*!< si_fd */
uint32_t ssi_tid; /*!< si_tid */
uint32_t ssi_band; /*!< si_band */
uint32_t ssi_overrun; /*!< si_overrun */
uint32_t ssi_trapno; /*!< si_trapno */
int32_t ssi_status; /*!< si_status */
int32_t ssi_svint; /*!< si_int */
uint64_t ssi_svptr; /*!< si_ptr */
uint64_t ssi_utime; /*!< si_utime */
uint64_t ssi_stime; /*!< si_stime */
uint64_t ssi_addr; /*!< si_addr */
uint8_t ssi_pad[48]; /*!< Pad size to 128 bytes (allow space for additional fields in the future) */
};
/*! \brief System call wrapper for \c signalfd(2).
\param fd File descriptor to modify (pass -1 to create a new file descriptor).
\param mask Signal mask. Any signals set in \a mask will be delivered through \a fd (but see below).
\retval 0 on success.
\retval -1 on error (and see \a errno).
System call wrapper function for \c signalfd(2). Any signals turned on in \a mask will be delivered
through the file descriptor \a fd. However, these signals should also be masked from the process
using \c sigprocmask(2), since otherwise the two delivery mechanisms will race.
Passing -1 in \a fd causes a new file descriptor to be allocated. It can be polled, closed and read
normally. It will return struct signalfd_siginfo objects when read. Passing an existing signalfd
file descriptor in \a fd will allow the \a mask to be modified.
\todo Eventually, this system call wrapper function will be removed in favour of \a glibc's
authoritative version.
*/
int signalfd(int fd, const sigset_t* mask);
/*! \brief Event-driven signal receiver object.
This object implements an event-driven signal receiver based on \c signalfd(2). The object contains
details such as the callback function for each received signal.
*/
struct lwevent_signal
#ifdef DOXYGEN
{
/*! \brief Dummy variable for doxygen; this structure is really opaque. */
int dummy;
}
#endif
;
/*! \brief Pointer to signal callback function. */
typedef void (*lwevent_signalcb)(const struct signalfd_siginfo* ssi);
/*! \brief Create and register event-driven signal receiver object.
\param mask Set of signals to catch.
\param callback Callback for received signals.
\returns Pointer to new receiver object.
\retval 0 on error (and see \a errno).
Creates a signal receiver file descriptor using \c signalfd(2), and requests that the signals in
\a mask are received through it. Also calls \c sigprocmask(2) to block the signals in \a mask, and
ensure the delivery mechanisms do not race.
This function will silently ignore attempts to receive \c SIGKILL, \c SIGSTOP, \c SIGBUS, \c SIGFPE,
\c SIGILL and \c SIGSEGV even if they are set in \a mask. Such signals either cannot be redirected
or would cause serious misbehaviour if they were not received immediately through the normal
mechanism.
*/
struct lwevent_signal* lwevent_signalfd(const sigset_t* mask, lwevent_signalcb callback)
__attribute__((malloc,nonnull,warn_unused_result));
/*! \brief Retrieve signal receiver's signal set. */
void lwevent_signalfd_getsigs(const struct lwevent_signal* si, sigset_t* mask)
__attribute__((nonnull));
/*! \brief Retrieve signal receiver's callback function. */
lwevent_signalcb lwevent_signalfd_getcb(const struct lwevent_signal* si)
__attribute__((nonnull));
/*! \brief Modify signal receiver's signal set.
\param si Signal receiver object.
\param mask The new mask to enact.
\param oldmask The old mask. May be 0.
\retval 0 on success.
\retval -1 on error (and see \a errno).
Changes the set of signals being received by \a si to those set in \a mask. See lwevent_signalfd()
for a discussion of the full behaviour of the receiver. The application may retrieve the previous
mask by passing a pointer to a signal set in \a oldmask.
*/
int lwevent_signalfd_setsigs(struct lwevent_signal* si, const sigset_t* mask, sigset_t* oldmask)
__attribute__((nonnull,warn_unused_result));
/*! \brief Modiy signal receiver's callback function. */
void lwevent_signalfd_setcb(struct lwevent_signal* si, lwevent_signalcb callback,
lwevent_signalcb* old_callback) __attribute__((nonnull));
/*! \brief Close signal receiver.
\param si Signal receiver object. May be 0.
Closes a signal receiver object, unblocking the signals it blocked with \c sigprocmask(2). If \a si
is null, this is a no-op.
*/
void lwevent_signalfd_close(struct lwevent_signal* si);
/*! \brief Set up a default signal receiver.
\returns Pointer to new receiver object.
\retval 0 on error (and see \a errno).
Sets up a default signal receiver object. This object will catch \c SIGINT and \c SIGTERM and set
the lwevent_loop_exit flag to non-zero. Other signals are left for the normal delivery mechanism.
*/
struct lwevent_signal* lwevent_signalfd_default(void) __attribute__((malloc,warn_unused_result));
/*!@}*/
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4:syntax=c.doxygen
*/