[Libevent-users] RFC - evdns client API
I'm trying to use the evdns client API for the first time (in tinc-1.1), and I've noticed a few things: 1. There's no threadsafe API (see ticket 1827379). This is not necessary for tinc but is good to have in general. 2. There's no way to cancel an outstanding resolve. (see ticket 1827307) this is important for tinc, which can stop a connection attempt for reasons of its own. I either need this or to allocate/track a resolver structure on my own so the callback doesn't crash the program. 3. There's a resolve function for each address family, so callers have to explicitly support IPv6 with extra code. This would not be necessary if libevent were to provide a getaddrinfo()-style interface. I propose a new API to address all of these points in a unified way. I have a starting point below. I only put in a cooked interface because that's what I would like for my current program, but I would not object to a raw interface that shares the same struct evdns_client and evdns_client_op. (In fact, one would be necessary internally to implement this side-by-side with the existing API.) My primary question to reviewers here is source vs. binary compatibility of future versions. I'm tempted to shoot for binary compatibility, but I'm not sure if it's worth it given that other libevent APIs (struct event, struct bufferevent) have chosen the other route. (struct evhttp_request seems to be on the fence - the structure is in the public header, but the comments suggest you should use accessors instead.) Here are the event_dns.h additions: /** * Create a new DNS resolver client. * @param base Operate with the given event_base * @param attrs Use the specified initialization attributes. * NULL is currently the only acceptable value. * (To reviewers: this is for future expansion; it * could hold evdns_resolv_conf_parse arguments, * among other things.) */ struct evdns_client *evdns_client_new(struct event_base *base, struct event_resolver_attr *attrs); /** * Free the DNS resolver client. * All outstanding operations will be canceled. */ void evdns_client_free(struct evdns_client *); /** * A cancelable DNS client operation. * As with struct event, the caller must keep this structure * in-place from evdns_resolve_*() to callback or * evdns_request_cancel(). * * (To reviewers: the definition should be fairly immutable for * binary compatibility; maybe just a pointer to the * struct request.) */ struct evdns_client_op; /** Cancels a pending DNS resolution. */ int evdns_client_op_cancel(struct evdns_client *, struct evdns_client_op *); /** * Results from evdns's cooked client resolver. * * (To reviewers: this is a structure rather than direct args * to allow future expansion without breaking source compatibility. * Because the caller never does the allocation, we could even * maintain binary compatibility by making this an opaque structure * with accessor functions.) */ struct evdns_cooked_results { /** * Time (in seconds) until cache expiry. * * (To reviewers: I'm considering the expiry directly available * as expiry and exposing gettime() as event_gettime() for * comparison. It seems most callers wanting to use the TTL * will have to do this translation in the callback, and * perhaps reimplement gettime() for a stable clock.) */ int ttl; /** * Individual results, which must be freed with struct evdns_cooked_result *results; }; /** * A single cooked result. * * (To reviewers: could also be opaqued for binary compatibility * with member accessors or even * evdns_cooked_result_{socket,connect,bind,str}().) */ struct evdns_cooked_result { struct evdns_cooked_result *next; /** * Socket family (for first argument of socket()). * (To reviewers: sa.sa_family may do instead? I'm not sure * why getaddrinfo() has a member like this.) */ int sock_family; /** Socket type (for second argument of socket()). */ int sock_type; /** for socket() */ /** Socket protocol (for third argument of socket()). */ int sock_protocol; /** * Socket length (for third argument of connect() and bind()). * Note sa.sa_len is not portable. */ socklen_t salen; struct sockaddr sa; /* MUST BE LAST - HIDDEN EXTRA SPACE HERE */ }; /** * Free cooked results. * @param res As with free() itself, may be NULL. */ void evdns_cooked_results_free(struct evdns_cooked_results *res); /** * Cooked results callback. * @param err Can be used with evdns_err_to_string * @param res If non-null, results. Must be freed with *evdns_cooked_results_free(). */ void (*evdns_cooked_resolve_cb_t)(int err, struct evdns_cooked_results *res, void *cbargs); /** * Default
[Libevent-users] epoll ADD, DEL
hi all I'am working on high performance server, and i need to do alot of (ADD and DEL fds) to and from the epoll does this will cause any performance problem thank you all ___ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users
[Libevent-users] [PATCH] signal.c: debug cleanups
1. Fix a debugging call with wrong format, (we should probably use __attr__((format(printf))) eventually). 2. Add additional debugging calls for sanity. -cl Index: signal.c === --- signal.c(revision 507) +++ signal.c(working copy) @@ -141,7 +141,7 @@ * a dynamic array is used to keep footprint on the low side. */ if (evsignal = sig-sh_old_max) { - event_debug((%s: evsignal sh_old_max, resizing array, + event_debug((%s: evsignal (%d) = sh_old_max (%d), resizing, __func__, evsignal, sig-sh_old_max)); sig-sh_old_max = evsignal + 1; p = realloc(sig-sh_old, sig-sh_old_max * sizeof *sig-sh_old); @@ -159,8 +159,9 @@ return (-1); } + /* setup new handler */ + event_debug((%s: %p: changing signal handler, __func__, ev)); #ifdef HAVE_SIGACTION - /* setup new handler */ memset(sa, 0, sizeof(sa)); sa.sa_handler = evsignal_handler; sa.sa_flags |= SA_RESTART; @@ -207,6 +208,7 @@ evsignal = EVENT_SIGNAL(ev); /* restore previous handler */ + event_debug((%s: %p: restoring signal handler, __func__, ev)); sh = sig-sh_old[evsignal]; sig-sh_old[evsignal] = NULL; #ifdef HAVE_SIGACTION ___ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users
[Libevent-users] [PATCH] event.c: make internal signal event count as active
[ WARNING: Long and ardorous - difficult to trace behaviour ] Behaviour described is without EV_PERSIST on internal signal handler (as it's been for months): 1. signal comes in, we jump to our internal signal handler, it writes to pipe. 2. epoll_wait() returns -1/EINTR because a signal handler was called. 3. we do not process epoll events here, we call evsignal_process() and return from dispatch. 4. our user-side event is deleted from queues, marked active, and processed normally BY evsignal_process(). 5. user-side event then adds itself back normally to handle signals again. 6. we head back into epoll_dispatch(), where epoll_wait() immediately returns because signal pipe is ready for reading (from step 1). 7. we implicitly delete the internal signal event (remember, this is without EV_PERSIST on it) and mark is as active. however, since it is an internal event, we do not increment event_count_active. 8. we return to our main event loop within event.c, and do not call event_process_active() because event_count_active == 0. this is where the problems really start. 9. we head back into epoll_dispatch() and wait normally. however, since we previously deleted our internal signal pipe fd from the epoll set, and did NOT call it from event_process_active() in the previous step - the pipe never ends up being read. but since the user callback was executed properly, everything looks normal. 10. we send the same signal again. epoll_wait() returns, and we call evsignal_process() again. 11. here's the funny part: evsignal_process() increments event_count_active - but since our internal signal event is actually first on the active queue at this point, when we go to process events in the main loop - we actually end up processing our internal signal event here. it reads from the pipe, and then re-adds itself back into epoll's watch set. but since it's internal, we do not decrement event_count_active - guaranteeing we'll get to our user event when we process the active events from the main loop. this is the flip-flop. 12. repeat. Now with EV_PERSIST on the internal signal event, steps 7-12 do not happen the first time the signal is called. So not only is the bug still there, it's super bad when it happens as epoll_wait() returns immediately. By sending a signal again - we can flip it from spin to wait. So this is a bug that needs to be fixed anyways. The EV_PERSIST change just made it more visible. I'm not so sure how to make a regression test for this other than a specific test to make sure that our signal pipe is empty AFTER returning from event_process_active(). -- test case: #include signal.h #include stdlib.h #include unistd.h #include event.h void scb(int sig, short event, void *a) { write(2, .\n, 2); event_add(a, NULL); return; } int main(void) { struct event sig; event_init(); event_set(sig, SIGTSTP, EV_SIGNAL, scb, sig); event_add(sig, NULL); event_dispatch(); return 0; } -- debug log (unpatched): $ ./sigtest [debug] event_add: event: 0x7fff762c0500,call 0x400768 [debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1, queue == 4 [debug] evsignal_add: evsignal sh_old_max, s == 20, max == 0 [debug] event_add: event: 0x602050, EV_READ call 0x2ac034a166d4 [debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 2 wait, signal [debug] evsignal_handler: wake up [debug] epoll_dispatch: epoll_wait() res == -1, errno = 4 [debug] evsignal_process: processing signals, caught == 1 [debug] event_del: 0x7fff762c0500, callback 0x400768 [debug] evsignal_del: 0x7fff762c0500: restoring previous handler [debug] event_active: 0x7fff762c0500: adding to active queue, ev_res == 0, res == 8 [debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1, queue == 8 [debug] event_base_loop: event_count_active == 1 . [debug] event_add: event: 0x7fff762c0500,call 0x400768 [debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1, queue == 4 [debug] epoll_dispatch: epoll_wait reports 1 [debug] event_del: 0x602050, callback 0x2ac034a166d4 [debug] event_active: 0x602050: adding to active queue, ev_res == 0, res == 2 [debug] event_queue_insert: 0x602050: docount == 0, event_count == 1, queue == 8 [debug] event_base_loop: event_count_active == 0 wait, signal (notice how we immediately call our internal event from event_base_loop?) [debug] evsignal_handler: wake up [debug] epoll_dispatch: epoll_wait() res == -1, errno = 4 [debug] evsignal_process: processing signals, caught == 1 [debug] event_del: 0x7fff762c0500, callback 0x400768 [debug] evsignal_del: 0x7fff762c0500: restoring previous handler [debug] event_active: 0x7fff762c0500: adding to active queue, ev_res == 8, res == 8 [debug] event_queue_insert: 0x7fff762c0500: docount == 1, event_count == 1, queue == 8 [debug] event_base_loop: event_count_active == 1 [debug] evsignal_cb: n == 2
Re: [Libevent-users] [PATCH] event.c: make internal signal event count as active
On Sat, Nov 10, 2007 at 06:44:12PM -0800, Christopher Layne wrote: 11. here's the funny part: evsignal_process() increments event_count_active - but since our should be: evsignal_process() adds the internal event to the active queue as normal. before we call event_process_active(), event_count_active is 1 at that point (user event). but since our ... internal signal event is actually first on the active queue at this point, when we go to process events in the main loop - we actually end up processing our internal signal event here. it reads from the pipe, and then re-adds itself back into epoll's watch set. but since it's internal, we do not decrement event_count_active - guaranteeing we'll get to our user event when we process the active events from the main loop. this is the flip-flop. -cl ___ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users
Re: [Libevent-users] C++ interface(s) [was: libev-1.3e...]
[note that i replied to the list, I hope this was ok, as I think a c++ interface *might* interest other users too. If anybody finds it too off-topic, tell me and I will repent :] On Fri, Nov 09, 2007 at 07:32:13PM +0100, Chris Brody [EMAIL PROTECTED] wrote: start () stop () and would complement libev nicely as its quite object-oriented in design. Yes-I definitely like this idea, with such an immediate re-use for a wide-spread application. I tried it, but found it was quite the nuisance and made the header file rather ugly. Since I am in immediate porting needs to replace my older iom.C solution in gvpe and urxvt, I started a c++ interface. It is very similar to iom.C (and also very similar to libev, of course), but provides a bit more fluffyness (you can call -set anytime and it will automatically stop/restart the event watcher). Its currently only useful for embedding (its in the files ev++.h and ev++.C), but I didn't chose to override the callback proper, so one can mix C users of libev and c++ users of the same. I am thinking about how to make some kind of thunk callback connector to go directly between libev and eventxx. This is what ev++.h also does, to keep full ABI compatibility. ev++.h is currently more or less providing simple wrappers around the watchers allowing object methods as callbacks. What is missing would be a nicer namespace cleanup (wrapper functions so ev_xxx becomes ev::xxx not ev::ev_) and a nice event loop and default loop abstraction. (I will probbaly switch to using enums for constants in ev.h at one point, although this is not quite as helpful as one would assume :) If you do make this one, I will also consider how to use it with eventxx. Not having analyzed eventxx in detail, I believe the interfaces to be quite different in style, with ev++ being quite a bit more primitive (except in its callback system which is more complex). Here is an example use (more or less straight from rxvt-unicode): struct term { void cursor_blink_cb (ev::timer w, int revents); ev::timer cursor_blink_ev; ... }; term::term () : cursor_blink_ev (this, term::cursor_blink_cb) { ... if (option (Opt_cursorBlink)) cursor_blink_ev.start (CURSOR_BLINK_INTERVAL, CURSOR_BLINK_INTERVAL); } void rxvt_term::cursor_blink_cb (ev::timer w, int revents) { hidden_cursor = !hidden_cursor; want_refresh = 1; } As you can see, there are aliases for start that also set the time - I did this because I had lots of code using the convention, and it doesn't seem to hurt the interface. For multiple event loops (not really tested), the constructor has an additional argument for the event loop to associate with (using the default event loop if not given), and also has a set method that changes association. This is neccessary because I decided that the destructor has to call stop(), which requires storing the event loop inside the watcher somewhere. Anyways, this is just a start to convert my existing projects, but it seems to work quite well. -- The choice of a Deliantra, the free code+content MORPG -==- _GNU_ http://www.deliantra.net ==-- _ generation ---==---(_)__ __ __ Marc Lehmann --==---/ / _ \/ // /\ \/ / [EMAIL PROTECTED] -=/_/_//_/\_,_/ /_/\_\ ___ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users
Re: [Libevent-users] [PATCH] event.c: make internal signal event count as active
On Sat, Nov 10, 2007 at 06:49:58PM -0800, Christopher Layne wrote: On Sat, Nov 10, 2007 at 06:44:12PM -0800, Christopher Layne wrote: 11. here's the funny part: evsignal_process() increments event_count_active - but since our Here's a better patch: This removes docount entirely. docount is used to determine if the event being added or removed from the queue should influence base-event_count. The internal signal event should not be counted as an event to wait for - such that when one deletes all their events the event loop will not count the internal signal event as something to wait around for (nothing changes here). However, based on the previous discussion, it still needs to be processed as a normal active event, hence we change base-event_count_active regardless of if it's internal or not. -cl Index: event.c === --- event.c (revision 507) +++ event.c (working copy) @@ -829,23 +829,17 @@ void event_queue_remove(struct event_base *base, struct event *ev, int queue) { - int docount = 1; - if (!(ev-ev_flags queue)) event_errx(1, %s: %p(fd %d) not on queue %x, __func__, ev, ev-ev_fd, queue); - if (ev-ev_flags EVLIST_INTERNAL) - docount = 0; - - if (docount) + if (~ev-ev_flags EVLIST_INTERNAL) base-event_count--; ev-ev_flags = ~queue; switch (queue) { case EVLIST_ACTIVE: - if (docount) - base-event_count_active--; + base-event_count_active--; TAILQ_REMOVE(base-activequeues[ev-ev_pri], ev, ev_active_next); break; @@ -866,8 +860,6 @@ void event_queue_insert(struct event_base *base, struct event *ev, int queue) { - int docount = 1; - if (ev-ev_flags queue) { /* Double insertion is possible for active events */ if (queue EVLIST_ACTIVE) @@ -877,17 +869,13 @@ ev, ev-ev_fd, queue); } - if (ev-ev_flags EVLIST_INTERNAL) - docount = 0; - - if (docount) + if (~ev-ev_flags EVLIST_INTERNAL) base-event_count++; ev-ev_flags |= queue; switch (queue) { case EVLIST_ACTIVE: - if (docount) - base-event_count_active++; + base-event_count_active++; TAILQ_INSERT_TAIL(base-activequeues[ev-ev_pri], ev,ev_active_next); break; ___ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users