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 Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users