Attached is a crack at supporting type-independent callback handlers. I've
gotten tired of stubbing out handlers everywhere.
It uses libffi (distributed with GCC), and GCC's type introspection
builtins. Strictly speaking, it'll never be "portable"; but in practice it
should be quite portable, at least in mainstream unix and win32
environments. It also shouldn't run afoul of non-executable stack
environments, but this is untested.
event_delegate() works like event_set() + event_add(). It takes the (struct
event_delegate) object, file descriptor, event set, timeout, callback
function (generic: void (*)()), and 0 to 7 parameters to pass when invoking
the callback.
delegate_init() initializes the object, and delegate_reset() should be
called at the end of the lifetime of the context; e.g. before control of fd
is passed away to, say, close(2). These requirements are just an artifact of
my implementation, not intrinsic to the concept.
TODO: Add magic parameter constants, which will be replaced on invocation
with a dynamic value. This would provide, for example, the ability to wrap
existing callbacks which have the (int), (short), (void *) signature (i.e.
where (short)events is passed as a parameter, the value for which is unknown
when calling event_delegate()). I'm sure there are real, nifty uses for such
a feature.
Ultimately, this gets the C developer quite close to something like
closures, which are so incredibly handy with event-oriented code in other
languages.
/* ==========================================================================
* delegate.c - Polymorphic libevent callbacks.
* --------------------------------------------------------------------------
* Copyright (c) 2009 William Ahern
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* --------------------------------------------------------------------------
*
* CAVEATS:
*
* o Only pass integral types as callback parameters, and not compound
* types like structures, unions or arrays. Pointers to compound
* types are fine. Passing arrays directly should behave like passing
* a pointer to the array, but caveat emptor.
*
* o Depends on pointer equivalence; or rather, that void pointers have
* the same underlying representation as any other pointer type.
*
* o Only works with GCC, or compilers which can impersonate well GCC.
*
* o Uses libffi, which is distributed with GCC.
*
* o Requires support for C99 compound literals; i.e. -std=gnu99 or
* -std=c99 (untested).
*
* o There shouldn't be side effects such as parameters being evaluated
* more than once, but caveat emptor.
*
* o Maximum of 7 callback parameters; minimum of 0.
*
* o event_delegate() lazily uninstalls the underlying libevent
* handler, since this is my preferred pattern with libevent. This
* has several impliciations:
*
* + EV_PERSIST is implicit, however unless you re-issue
* event_delegate(), the handler will be removed
* automatically after a callback. You can call
* event_delegate() as many times as you want, however, w/o
* side effect.
*
* + delegate_reset() or delegate_del() MUST ALWAYS BE CALLED
* BEFORE the underlying object memory is deallocted AND
* BEFORE the file descriptor is closed or ownership passed;
* otherwise, delegate_fwd() (internal function) will
* dereference and potentially corrupt invalid memory and/or
* spuriously remove an epoll/kqueue association.
*
* + Depending on your platform and version of libevent, you
* must explicitly call delegate_reset() or delegate_del()
* before any other code can re-register the underlying
* descriptor itself.
*
* In practical terms, this merely means you must reset the delegate
* object at the same time you pass away control of--or destroy--the
* underlying memory and/or file descriptor. Which in practice is
* intuitive, IMO.
*
* USAGE:
*
* 1) struct event_delegate del = DELEGATE_INITIALIZER
* or
* delegate_init(&del)
*
* 2) int error; // 0 on success, EINVAL, ENOMEM, etc. on failure.
* int fd; // Valid descriptor, or -1 if setting timeout only.
* short events; // EV_READ and/or EV_WRITE; EV_PERSIST is implicit.
* struct timeval timeout;
* void (*cb)();
*
* error = event_delegate(&del, fd, events, &timeout, cb, arg0, ...)
*
* 3) delegate_reset(&del) // Always do this before deallocating
* or // underlying memory or losing control of
* delegate_del(&del) // the desctiptor, because delegate_add()
* // uses EV_PERSIST, but delegate_fwd() lazily
* // uninstalls the libevent handler unless
* // event_delegate() is called again.
*
* 4) Link with libffi: -lffi.
*
* EXAMPLE:
*
* #include <stdlib.h>
* #include <stdio.h>
*
* #include <unistd.h>
*
* #include <event.h>
*
* #include "delegate.h"
*
* void event_catch(char *str, short h, long long ll, struct event_delegate
*del) {
* printf("%s, %hd, %lld, %p\n", str, h, ll, (void *)del);
* }
*
* int main(void) {
* struct event_delegate del = DELEGATE_INITIAILIZER;
* int fd = FILENO_STDIN;
* short events = EV_READ;
* struct timeval timeout = { 3, 0 };
* int error, okay;
*
* event_init();
*
* error = event_delegate(&del, fd, events, &timeout, &event_catch,
* "hello world", (short)0xf00f, 42ULL, &del);
*
* if (error)
* // ...
*
* okay = !event_dispatch();
*
* return (okay)? EXIT_SUCCESS : EXIT_FAILURE;
* }
*
* ==========================================================================
*/
#include <errno.h> /* EINVAL errno */
#include <sys/time.h> /* struct timeval */
#include <event.h> /* EV_READ EV_WRITE event_set(3) event_add(3)
event_del(3) event_pending(3) */
#include "delegate.h"
#ifndef lengthof
#define lengthof(a) (sizeof (a) / sizeof (a)[0])
#endif
void delegate_del(struct event_delegate *del) {
if (del->uninstall) {
*del->uninstall = 0;
del->uninstall = 0;
}
if (event_pending(&del->event, EV_READ|EV_WRITE, 0))
event_del(&del->event);
del->fd = -1;
del->events = 0;
if (evtimer_pending(&del->timer, 0))
event_del(&del->timer);
del->timeout = (struct timeval){ 0 };
} /* delegate_del() */
static void delegate_fwd(int fd, short events, void *arg) {
int uninstall = 1;
struct event_delegate *del = arg;
void *args[lengthof(del->cb.args)];
int argc;
ffi_arg retval;
if (del->uninstall)
*del->uninstall = 0;
del->uninstall = &uninstall;
for (argc = 0; argc < del->cb.argc; argc++)
args[argc] = &del->cb.args[argc];
ffi_call(&del->cb.cif, FFI_FN(del->cb.fn), &retval, args);
if (uninstall)
delegate_del(del);
} /* delegate_fwd() */
int delegate_add(struct event_delegate *del, int fd, short events, struct
timeval *timeout, void (*cb)(), int argc) {
int error;
if (del->uninstall) {
*del->uninstall = 0;
del->uninstall = 0;
}
if (cb) {
if (FFI_OK != ffi_prep_cif(&del->cb.cif, FFI_DEFAULT_ABI, argc,
&ffi_type_void, del->cb.types)) {
error = EINVAL;
goto error;
}
del->cb.fn = cb;
del->cb.argc = argc;
}
if (del->fd != fd || del->events != events)
delegate_del(del);
if (evtimer_pending(&del->timer, 0)) {
event_del(&del->timer);
del->timeout = (struct timeval){ 0 };
}
if (fd != -1 && events) {
event_set(&del->event, fd, events|EV_PERSIST, &delegate_fwd,
del);
if (0 != event_add(&del->event, 0))
goto syerr;
del->fd = fd;
del->events = events;
}
if (timeout) {
evtimer_set(&del->timer, &delegate_fwd, del);
evtimer_add(&del->timer, timeout);
del->timeout = *timeout;
}
return 0;
syerr:
error = errno;
error:
delegate_del(del);
return error;
} /* delegate_add() */
int delegate_pending(struct event_delegate *del, short events, struct timeval
*timeout) {
if (events & (EV_READ|EV_WRITE)) {
if (event_pending(&del->event, events & (EV_READ|EV_WRITE),
timeout))
return 1;
}
if (events & EV_TIMEOUT) {
if (event_pending(&del->timer, events & EV_TIMEOUT, timeout))
return 1;
}
return 0;
} /* delegate_pending() */
/* ==========================================================================
* delegate.h - Polymorphic libevent callbacks.
* --------------------------------------------------------------------------
* Copyright (c) 2009 William Ahern
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#ifndef DELEGATE_H
#define DELEGATE_H
#include <stdint.h>
#include <string.h> /* memcpy(3) */
#include <event.h> /* EV_READ EV_WRITE struct event */
#include <ffi/ffi.h>
#define ffi_typeof(p) __typeof__( \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), uint8_t), \
(uint8_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), int8_t), \
(int8_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), uint16_t), \
(uint16_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), int16_t), \
(int16_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), uint32_t), \
(uint32_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), int32_t), \
(int32_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), uint64_t), \
(uint64_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), int64_t), \
(int64_t){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), float), \
(float){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), double), \
(double){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), long double), \
(long double){ 0 }, \
__builtin_choose_expr( \
__builtin_types_compatible_p(__typeof__(p), void *), \
(void *){ 0 }, (void *){ 0 } \
)))))))))))))
#define ffi_type(p) \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), uint8_t), \
&ffi_type_uint8, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), int8_t), \
&ffi_type_sint8, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), uint16_t), \
&ffi_type_uint16, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), int16_t), \
&ffi_type_sint16, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), uint32_t), \
&ffi_type_uint32, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), int32_t), \
&ffi_type_sint32, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), uint64_t), \
&ffi_type_uint64, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), int64_t), \
&ffi_type_sint64, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), float), \
&ffi_type_float, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), double), \
&ffi_type_double, \
__builtin_choose_expr( \
__builtin_types_compatible_p(ffi_typeof(p), void *), \
&ffi_type_pointer, &ffi_type_void \
)))))))))))
#define DELEGATE_NARG_(a, b, c, d, e, f, g, N,...) N
#define DELEGATE_NARG(...) DELEGATE_NARG_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define DELEGATE_INITIALIZER { -1, 0 }
struct event_delegate {
int fd;
short events;
struct timeval timeout;
int *uninstall;
struct {
void (*fn)();
int argc;
union {
long long ll;
unsigned long long llu;
void *p;
long double g;
} args[7];
ffi_type *types[7];
ffi_cif cif;
} cb;
struct event event, timer;
}; /* struct event_delegate */
#define delegate_copyarg(del, argn, arg) \
({ memcpy(&(del)->cb.args[(argn)], &(arg), sizeof (ffi_typeof(arg))); \
(del)->cb.types[(argn)] = ffi_type(arg); })
#define delegate_setarg(del, argn, arg) \
delegate_copyarg((del), (argn), (ffi_typeof((arg))){(arg)})
// delegate_copyarg((del), (argn),
__builtin_choose_expr(__builtin_constant_p((arg)), (ffi_typeof((arg))){(arg)},
(arg)))
#define event_delegate7(del, fd, events, timeout, fn, argc, arg0, arg1, arg2,
arg3, arg4, arg5, arg6, ...) \
({ __builtin_choose_expr(((argc) > 0), delegate_setarg((del), 0,
(arg0)), (void)0); \
__builtin_choose_expr(((argc) > 1), delegate_setarg((del), 1,
(arg1)), (void)0); \
__builtin_choose_expr(((argc) > 2), delegate_setarg((del), 2,
(arg2)), (void)0); \
__builtin_choose_expr(((argc) > 3), delegate_setarg((del), 3,
(arg3)), (void)0); \
__builtin_choose_expr(((argc) > 4), delegate_setarg((del), 4,
(arg4)), (void)0); \
__builtin_choose_expr(((argc) > 5), delegate_setarg((del), 5,
(arg5)), (void)0); \
__builtin_choose_expr(((argc) > 6), delegate_setarg((del), 6,
(arg6)), (void)0); \
delegate_add((del), (fd), (events), (timeout), (fn), (argc)); })
#define event_delegate(del, fd, events, timeout, fn, ...) \
event_delegate7((del), (fd), (events), (timeout), (fn),
DELEGATE_NARG(__VA_ARGS__), __VA_ARGS__, 0, 0, 0, 0, 0, 0, 0)
int delegate_add(struct event_delegate *, int, short, struct timeval *, void
(*)(), int);
void delegate_del(struct event_delegate *);
int delegate_pending(struct event_delegate *, short, struct timeval *);
static __inline__ struct event_delegate *delegate_init(struct event_delegate
*del)
{ *del = (struct event_delegate)DELEGATE_INITIALIZER; return del; }
static __inline__ struct event_delegate *delegate_reset(struct event_delegate
*del)
{ delegate_del(del); return delegate_init(del); }
#endif /* DELEGATE_H */
_______________________________________________
Libevent-users mailing list
[email protected]
http://monkeymail.org/mailman/listinfo/libevent-users