[Libevent-users] RFC - evdns client API

2007-11-10 Thread Scott Lamb
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

2007-11-10 Thread abdullah khalili
 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

2007-11-10 Thread Christopher Layne
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

2007-11-10 Thread Christopher Layne
[ 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

2007-11-10 Thread Christopher Layne
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...]

2007-11-10 Thread Marc Lehmann
[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

2007-11-10 Thread Christopher Layne
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