WIP
This commit is contained in:
parent
938852afd3
commit
60b9d9cc48
|
@ -9,6 +9,8 @@
|
|||
#define HEADER_liblwevent
|
||||
|
||||
/* 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
|
||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/syscall.h> /* for 400_signalfd.c */
|
||||
|
||||
/* options for text editors
|
||||
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
|
||||
|
|
|
@ -8,17 +8,18 @@
|
|||
|
||||
|
||||
/* _lwevent_activate()
|
||||
* Activates the event `ev'. Called when creating a new event in lwevent_new() or when reactivating
|
||||
* an event in lwevent_reactivate(). Calls epoll_ctl(EPOLL_CTL_ADD).
|
||||
* Activates the event `ev'. Called with EPOLL_CTL_ADD in `op' when creating a new event in
|
||||
* 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;
|
||||
|
||||
ee.events = ev->events;
|
||||
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->callback = callback;
|
||||
|
||||
if(_lwevent_activate(ev)) {
|
||||
if(_lwevent_activate(EPOLL_CTL_ADD, ev)) {
|
||||
free(ev);
|
||||
return 0;
|
||||
}
|
||||
|
@ -79,7 +80,7 @@ int lwevent_deactivate(struct lwevent* ev)
|
|||
*/
|
||||
int lwevent_reactivate(struct lwevent* ev)
|
||||
{
|
||||
return _lwevent_activate(ev);
|
||||
return _lwevent_activate(EPOLL_CTL_ADD, ev);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,14 +28,13 @@ retrieve associated values.
|
|||
|
||||
*/
|
||||
struct lwevent
|
||||
#ifndef DOXYGEN
|
||||
;
|
||||
#else
|
||||
#ifdef DOXYGEN
|
||||
{
|
||||
/*! \brief Dummy variable for doxygen */
|
||||
int dummy;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
*/
|
|
@ -1,4 +1,4 @@
|
|||
/* liblwevent/src/liblwevent/300_fdevent.h
|
||||
/* liblwevent/src/liblwevent/300_fdparams.h
|
||||
*
|
||||
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
|
||||
* 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. */
|
||||
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.
|
||||
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
*/
|
|
@ -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
|
||||
*/
|
Loading…
Reference in New Issue