From anchie at fer.hr Mon Feb 4 09:24:38 2008 From: anchie at fer.hr (Ana Kukec) Date: Mon Feb 4 09:25:15 2008 Subject: [Libevent-users] connect(), EV_WRITE event Message-ID: <47A72026.6060800@fer.hr> Hi all, I tried to use the libevent library (on FreeBSD 7.0) to detect when the socket becomes writeable. Actually, i want to detect if destination host is reachable and has started tcp listener on desired port: # if ( connect(fd, ...) < 0 ) # perror("connect error"); # event_set(&ev_connect, client_s, EV_WRITE, fn, ...); # event_add(&ev_connect, NULL); where the function fn is: # void fn (...) { # cout << "Function fn!" << endl; # } When i tried to connect to reachable host (but without tcp listener on desired port), the situation is following: connect error: Connection refused Function fn! (*program enters immediately into function fn*) On the other hand, when i try to connect to unreachable host i get the following: connect error: Operation now in progress Function fn! (*after a few seconds*) So, I have two questions. How to use libevent (in case of reachable host) to detect that the other host really has started tcp listener on desired port? And the second one.. What is the timeout period after which program enters the function fn? Tnx in advance, -- Ana Kukec, B.Sc, ZTEL , FER , http://ana.kukec.net From wouter at nlnetlabs.nl Wed Feb 6 02:27:15 2008 From: wouter at nlnetlabs.nl (W.C.A. Wijngaards) Date: Wed Feb 6 02:27:56 2008 Subject: [Libevent-users] Solaris trouble Message-ID: <47A96153.7000202@nlnetlabs.nl> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, On Solaris 5.11 I get the following errors from libevent (trunk). This code creates runloops, adds a couple fd watchers, removes them. It happens both with evport and devpoll. (with evport:) port_dissociate: No such file or directory ~ error: event_del error port_dissociate: No such file or directory ~ error: event_del error port_dissociate: No such file or directory .. lots more errors port_getn: Timer expired fatal error: event_dispatch returned error -1, errno is Timer expired The code works fine on BSD and Linux. Best regards, ~ Wouter -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFHqWFTkDLqNwOhpPgRAgQkAKClTDTH1NPLvaEJM7ns1kohj+dccQCeJRgH SZYlxddeEpn8baGxkzDNu3Q= =XgV/ -----END PGP SIGNATURE----- From wouter at nlnetlabs.nl Wed Feb 6 02:40:25 2008 From: wouter at nlnetlabs.nl (W.C.A. Wijngaards) Date: Wed Feb 6 02:40:36 2008 Subject: [Libevent-users] Solaris trouble In-Reply-To: <47A96153.7000202@nlnetlabs.nl> References: <47A96153.7000202@nlnetlabs.nl> Message-ID: <47A96469.4000308@nlnetlabs.nl> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Apologies, the errors below are from libevent-1.2a. libevent trunk works. Best regards, ~ Wouter W.C.A. Wijngaards wrote: | Hi, | | On Solaris 5.11 I get the following errors from libevent (trunk). | This code creates runloops, adds a couple fd watchers, removes them. | It happens both with evport and devpoll. | | (with evport:) | port_dissociate: No such file or directory | ~ error: event_del error | port_dissociate: No such file or directory | ~ error: event_del error | port_dissociate: No such file or directory | .. lots more errors | port_getn: Timer expired | fatal error: event_dispatch returned error -1, errno is Timer expired | | The code works fine on BSD and Linux. | | Best regards, | ~ Wouter _______________________________________________ Libevent-users mailing list Libevent-users@monkey.org http://monkeymail.org/mailman/listinfo/libevent-users -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFHqWRpkDLqNwOhpPgRAkcEAJ0bywzfEJYOgWsK/qd1DpKj0Wk26gCfURf4 +nQPIWk+cdnvgOrAU4lkcos= =E0kj -----END PGP SIGNATURE----- From wouter at nlnetlabs.nl Wed Feb 6 05:50:07 2008 From: wouter at nlnetlabs.nl (W.C.A. Wijngaards) Date: Wed Feb 6 05:50:15 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN Message-ID: <47A990DF.9050004@nlnetlabs.nl> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi, port_getn on Solaris 5.11 can return EAGAIN (even though this is not documented). Patch to handle this and remove two unused variables. Index: evport.c =================================================================== - --- evport.c (revision 628) +++ evport.c (working copy) @@ -234,10 +234,8 @@ ~ grow(struct evport_data *epdp, int factor) ~ { ~ struct fd_info *tmp; - - struct fd_info *old = epdp->ed_fds; ~ int oldsize = epdp->ed_nevents; ~ int newsize = factor * oldsize; - - int ii; ~ assert(factor > 1); ~ check_evportop(epdp); @@ -332,7 +330,7 @@ ~ if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, ~ (unsigned int *) &nevents, ts_p)) == -1) { - - if (errno == EINTR) { + if (errno == EINTR || errno == EAGAIN) { ~ evsignal_process(base); ~ return (0); ~ } else if (errno == ETIME) { -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFHqZDfkDLqNwOhpPgRAgLbAJ44rLKOXLGE1S9n44wDFdV+ByKOmgCgmiGf gHuBW/Pmh9jIYHaH6A/rD3M= =Nopb -----END PGP SIGNATURE----- From provos at citi.umich.edu Wed Feb 6 11:15:21 2008 From: provos at citi.umich.edu (Niels Provos) Date: Wed Feb 6 11:15:36 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN In-Reply-To: <47A990DF.9050004@nlnetlabs.nl> References: <47A990DF.9050004@nlnetlabs.nl> Message-ID: <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> Thanks for the patch. I fixed trunk. Could you please verify that this is working for you in trunk? I can back port to the 1.4 branch then. Niels. On Feb 6, 2008 2:50 AM, W.C.A. Wijngaards wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi, > > port_getn on Solaris 5.11 can return EAGAIN (even though this is not > documented). Patch to handle this and remove two unused variables. > > Index: evport.c > =================================================================== > - --- evport.c (revision 628) > +++ evport.c (working copy) > @@ -234,10 +234,8 @@ > ~ grow(struct evport_data *epdp, int factor) > ~ { > ~ struct fd_info *tmp; > - - struct fd_info *old = epdp->ed_fds; > ~ int oldsize = epdp->ed_nevents; > ~ int newsize = factor * oldsize; > - - int ii; > ~ assert(factor > 1); > > ~ check_evportop(epdp); > @@ -332,7 +330,7 @@ > > ~ if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN, > ~ (unsigned int *) &nevents, ts_p)) == -1) { > - - if (errno == EINTR) { > + if (errno == EINTR || errno == EAGAIN) { > ~ evsignal_process(base); > ~ return (0); > ~ } else if (errno == ETIME) { > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v1.4.7 (GNU/Linux) > Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org > > iD8DBQFHqZDfkDLqNwOhpPgRAgLbAJ44rLKOXLGE1S9n44wDFdV+ByKOmgCgmiGf > gHuBW/Pmh9jIYHaH6A/rD3M= > =Nopb > -----END PGP SIGNATURE----- > _______________________________________________ > Libevent-users mailing list > Libevent-users@monkey.org > http://monkeymail.org/mailman/listinfo/libevent-users > > From wouter at NLnetLabs.nl Thu Feb 7 03:15:46 2008 From: wouter at NLnetLabs.nl (Wouter Wijngaards) Date: Thu Feb 7 03:16:19 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN In-Reply-To: <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> References: <47A990DF.9050004@nlnetlabs.nl> <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> Message-ID: <47AABE32.1020503@nlnetlabs.nl> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Niels, Thanks for accepting the patch. It works for me :-) Tried make verify with fixed trunk on solaris 5.11 and it errors Event ports: Testing Priorities 1: OK Testing Priorities 2: OK Testing Priorities 3: OK Testing Evbuffer: OK Testing evbuffer_readln(): OK Testing evbuffer_find 1: OK Testing evbuffer_find 2: OK Testing evbuffer_find 3: OK Bufferevent: OK Free active base: OK Event base new: OK After fork: ^C [ it hangs ] dev poll Testing Priorities 1: OK Testing Priorities 2: OK Testing Priorities 3: OK Testing Evbuffer: OK Testing evbuffer_readln(): OK Testing evbuffer_find 1: OK Testing evbuffer_find 2: OK Testing evbuffer_find 3: OK Bufferevent: OK Free active base: OK Event base new: OK After fork: [warn] ioctl: DP_POLL: Permission denied FAILED (exit) The libevent trunk without the fix has the exact same errors. Best regards, ~ Wouter Niels Provos wrote: | Thanks for the patch. I fixed trunk. Could you please verify that | this is working for you in trunk? I can back port to the 1.4 branch | then. | | Niels. | | On Feb 6, 2008 2:50 AM, W.C.A. Wijngaards wrote: | Hi, | | port_getn on Solaris 5.11 can return EAGAIN (even though this is not | documented). Patch to handle this and remove two unused variables. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFHqr4ykDLqNwOhpPgRArm5AKCSl+k/ePjoUSxhreLNEC45DU+BlACgg5sc +TU/8AQZUoWMGWqFcxKrIOs= =RHlg -----END PGP SIGNATURE----- From provos at citi.umich.edu Thu Feb 7 10:51:01 2008 From: provos at citi.umich.edu (Niels Provos) Date: Thu Feb 7 10:51:12 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN In-Reply-To: <47AABE32.1020503@nlnetlabs.nl> References: <47A990DF.9050004@nlnetlabs.nl> <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> <47AABE32.1020503@nlnetlabs.nl> Message-ID: <850f7cbe0802070751l438da1ccr66dc6f2d1db0dc9c@mail.gmail.com> Any chance that you could try to append "1 /* need reinit */" at the end of struct evportops and devpollops; see kqueue.c for an example. Then run the tests again. Thank you, Niels. On Feb 7, 2008 12:15 AM, Wouter Wijngaards wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > Hi Niels, > > Thanks for accepting the patch. It works for me :-) > > Tried make verify with fixed trunk on solaris 5.11 and it errors > > Event ports: > Testing Priorities 1: OK > Testing Priorities 2: OK > Testing Priorities 3: OK > Testing Evbuffer: OK > Testing evbuffer_readln(): OK > Testing evbuffer_find 1: OK > Testing evbuffer_find 2: OK > Testing evbuffer_find 3: OK > Bufferevent: OK > Free active base: OK > Event base new: OK > After fork: ^C [ it hangs ] > > dev poll > Testing Priorities 1: OK > Testing Priorities 2: OK > Testing Priorities 3: OK > Testing Evbuffer: OK > Testing evbuffer_readln(): OK > Testing evbuffer_find 1: OK > Testing evbuffer_find 2: OK > Testing evbuffer_find 3: OK > Bufferevent: OK > Free active base: OK > Event base new: OK > After fork: [warn] ioctl: DP_POLL: Permission denied > FAILED (exit) > > The libevent trunk without the fix has the exact same errors. > > Best regards, > ~ Wouter > > Niels Provos wrote: > | Thanks for the patch. I fixed trunk. Could you please verify that > | this is working for you in trunk? I can back port to the 1.4 branch > | then. > | > | Niels. > | > | On Feb 6, 2008 2:50 AM, W.C.A. Wijngaards wrote: > | Hi, > | > | port_getn on Solaris 5.11 can return EAGAIN (even though this is not > | documented). Patch to handle this and remove two unused variables. > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v1.4.7 (GNU/Linux) > Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org > > iD8DBQFHqr4ykDLqNwOhpPgRArm5AKCSl+k/ePjoUSxhreLNEC45DU+BlACgg5sc > +TU/8AQZUoWMGWqFcxKrIOs= > =RHlg > -----END PGP SIGNATURE----- > > From wouter at NLnetLabs.nl Fri Feb 8 02:57:17 2008 From: wouter at NLnetLabs.nl (Wouter Wijngaards) Date: Fri Feb 8 02:57:34 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN In-Reply-To: <850f7cbe0802070751l438da1ccr66dc6f2d1db0dc9c@mail.gmail.com> References: <47A990DF.9050004@nlnetlabs.nl> <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> <47AABE32.1020503@nlnetlabs.nl> <850f7cbe0802070751l438da1ccr66dc6f2d1db0dc9c@mail.gmail.com> Message-ID: <47AC0B5D.1040108@nlnetlabs.nl> -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Niels, Tested with that change and that fixes it. (I tested without http, rpc and dns tests). To be sure, below is what I did. Best regards, ~ Wouter + evport_dealloc, + 1 /* need reinit */ + devpoll_dealloc, + 1 /* need reinit */ Niels Provos wrote: | Any chance that you could try to append "1 /* need reinit */" at the | end of struct evportops and devpollops; see kqueue.c for an example. | Then run the tests again. | | Thank you, | Niels. | | On Feb 7, 2008 12:15 AM, Wouter Wijngaards wrote: | Hi Niels, | | Thanks for accepting the patch. It works for me :-) | | Tried make verify with fixed trunk on solaris 5.11 and it errors | | Event ports: | After fork: ^C [ it hangs ] | | dev poll | After fork: [warn] ioctl: DP_POLL: Permission denied | FAILED (exit) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFHrAtdkDLqNwOhpPgRAhIEAJ9hpJ8LBbvQn5X/v+szFAulzQ0w3wCgrD7o zyPJEg7fkLrRznuoB7PANgA= =OFkR -----END PGP SIGNATURE----- From flo at rfc822.org Sat Feb 9 07:33:32 2008 From: flo at rfc822.org (Florian Lohoff) Date: Sat Feb 9 08:05:41 2008 Subject: [Libevent-users] Documentation of object ownership/lifecycle Message-ID: <20080209123332.GA17129@paradigm.rfc822.org> Hi, i was once again reading the manpage and the doxygen documentation on the webpages but i was really confused on whether e.g. "bufferevent_write_buffer" takes over ownership of the passed evbuffer. I mean evbuffer_new is the source of the evbuffers but who is the consumer or sink? Its hard to understand from the documentation what the lifecycle of an evbuffer is within the callbacks, evbuffer and bufferevent API. I mean - i find my way by gdb and valgrind - either crash or memleak shows the misusage of the API but i guess fixing the documentation would be helpful to a lot of interested partys. But anyway - thanks for libevent - great tool i dont want to miss. Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: Digital signature Url : http://monkeymail.org/archives/libevent-users/attachments/20080209/84e6ce0a/attachment.bin From provos at citi.umich.edu Sat Feb 9 14:41:47 2008 From: provos at citi.umich.edu (Niels Provos) Date: Sat Feb 9 14:41:55 2008 Subject: [Libevent-users] Documentation of object ownership/lifecycle In-Reply-To: <20080209123332.GA17129@paradigm.rfc822.org> References: <20080209123332.GA17129@paradigm.rfc822.org> Message-ID: <850f7cbe0802091141r7fdb2ebbuf07e3c56731afc9b@mail.gmail.com> Please, take a look at the doxygen documentation. As for evbuffer - you never loose ownership of it, the data usually just gets removed from it. The documentation could be clearer on that. Niels. On Feb 9, 2008 4:33 AM, Florian Lohoff wrote: > > Hi, > > i was once again reading the manpage and the doxygen documentation on the > webpages but i was really confused on whether e.g. "bufferevent_write_buffer" > takes over ownership of the passed evbuffer. I mean evbuffer_new is the > source of the evbuffers but who is the consumer or sink? > > Its hard to understand from the documentation what the lifecycle of an > evbuffer is within the callbacks, evbuffer and bufferevent API. > > I mean - i find my way by gdb and valgrind - either crash or memleak > shows the misusage of the API but i guess fixing the documentation > would be helpful to a lot of interested partys. > > But anyway - thanks for libevent - great tool i dont want to miss. > > Flo > -- > Florian Lohoff flo@rfc822.org +49-171-2280134 > Those who would give up a little freedom to get a little > security shall soon have neither - Benjamin Franklin > > -----BEGIN PGP SIGNATURE----- > Version: GnuPG v1.4.6 (GNU/Linux) > > iD8DBQFHrZ2cUaz2rXW+gJcRAlkyAJ49RHE5wJbOx1ZOPAH3f0YwyTkRGQCeKsjV > QHaNxQUTddbnGVEJSHscrUQ= > =Sw/z > -----END PGP SIGNATURE----- > > _______________________________________________ > Libevent-users mailing list > Libevent-users@monkey.org > http://monkeymail.org/mailman/listinfo/libevent-users > > From flo at rfc822.org Sun Feb 10 02:55:53 2008 From: flo at rfc822.org (Florian Lohoff) Date: Sun Feb 10 03:00:34 2008 Subject: [Libevent-users] Documentation of object ownership/lifecycle In-Reply-To: <850f7cbe0802091141r7fdb2ebbuf07e3c56731afc9b@mail.gmail.com> References: <20080209123332.GA17129@paradigm.rfc822.org> <850f7cbe0802091141r7fdb2ebbuf07e3c56731afc9b@mail.gmail.com> Message-ID: <20080210075553.GA30784@paradigm.rfc822.org> On Sat, Feb 09, 2008 at 11:41:47AM -0800, Niels Provos wrote: > Please, take a look at the doxygen documentation. > > As for evbuffer - you never loose ownership of it, the data usually > just gets removed from it. The documentation could be clearer on > that. Ahh - okay - I get it ... But the doxygen Documentation mentions about nil concerning the usage of evbuffers just how to allocate, free get and put bytes into it. Something like this in the evbuffer_new would be helpful. "An evbuffer stays always in the responsibility of the caller and it needs to be freed with evbuffer_free() after usage." And for bufferevent_write_buffer: "Write data from an evbuffer to a bufferevent buffer. The data will be removed from the evbuffer which may be reused." Flo -- Florian Lohoff flo@rfc822.org +49-171-2280134 Those who would give up a little freedom to get a little security shall soon have neither - Benjamin Franklin -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: Digital signature Url : http://monkeymail.org/archives/libevent-users/attachments/20080210/a52a90e1/attachment.bin From slamb at slamb.org Sun Feb 10 06:34:18 2008 From: slamb at slamb.org (Scott Lamb) Date: Sun Feb 10 06:34:48 2008 Subject: [Libevent-users] evhttp changes for large requests Message-ID: <47AEE13A.6030808@slamb.org> I've written a simple libevent-based HTTP proxy, and now I'd like it to work with large requests: $ curl --proxy localhost:8080 \ http://localhost/hugefile $ curl --proxy localhost:8080 --max-time 1 \ http://localhost/hugefile $ curl --proxy localhost:8080 --limit-rate 1k \ http://localhost/hugefile To do that I need some changes. I have some preliminary patches (no tests yet). (1) Use a 64-bit counter for tracking the Content-Length:. Patch attached. (Somehow this patch isn't working, though? curl complains that it's stopping at 2^31-1 bytes. I may debug later.) (2) Stream incoming responses. Always call a non-NULL chunk_cb when we get more data, not just when using chunked encoding. Patch attached. (3) Stream large responses (to avoid huge memory use and high latency) with "Content-Length:" rather than "Transfer-Encoding: chunked" when possible so that client-side progress indicators work properly. Patch attached. (4) Detect while streaming that the client connection has been lost, so that we know to stop sending chunks. Patch attached to return error code in evhttp_send_reply_chunk(). (Wouldn't hurt to add error returns in other places, but this is my immediate need.) (5) Abort an outgoing request. Maybe evhttp_request_free() should do this if the request is active? or add an explicit evhttp_request_abort()? (6) Flow control (for the --limit-rate case). I need to keep the outgoing connection input buffer, outgoing request input buffer, incoming request output buffer, and incoming connection output buffer from growing to be ridiculous. I'm considering something like this: * define read watermarks on requests; it will only read from the outgoing connection when the input buffers are below the high (default of ~0 to preserve existing behavior). it will only call the chunk callback when the input buffers are above the low (default of 0). * define an option to drain the input buffer after calling the chunk callback, defaulting to 1 for existing behavior. I'd want to turn it off so I can transfer bytes only if the output buffers are not full. * define output watermarks as well. If below low watermark (defaulting to 0), call a fill function which in my case would also transfer from input buffers. I'm not too attached to this plan. I keep thinking there might be a simpler way, but I don't see it. Best regards, Scott -- Scott Lamb -------------- next part -------------- evhttp: stream incoming responses, even if not chunked From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/http.c | 19 ++++++++++++++----- 1 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libevent/http.c b/libevent/http.c index 07d1567..94c3a0f 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -730,11 +730,6 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) EVBUFFER_DATA(buf), req->ntoread); evbuffer_drain(buf, req->ntoread); req->ntoread = -1; - if (req->chunk_cb != NULL) { - (*req->chunk_cb)(req, req->cb_arg); - evbuffer_drain(req->input_buffer, - EVBUFFER_LENGTH(req->input_buffer)); - } } return (0); @@ -768,7 +763,21 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) req->ntoread = 0; evhttp_connection_done(evcon); return; + } else if (req->chunk_cb != NULL) { + /* + * The condition above is simply for efficiency; we do the + * move at the end if we aren't streaming. + */ + req->ntoread -= EVBUFFER_LENGTH(buf); + evbuffer_add_buffer(req->input_buffer, buf); } + + if (EVBUFFER_LENGTH(req->input_buffer) > 0 && req->chunk_cb != NULL) { + (*req->chunk_cb)(req, req->cb_arg); + evbuffer_drain(req->input_buffer, + EVBUFFER_LENGTH(req->input_buffer)); + } + /* Read more! */ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon); EVHTTP_BASE_SET(evcon, &evcon->ev); -------------- next part -------------- evhttp: stream large responses without requiring chunking From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/http.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libevent/http.c b/libevent/http.c index 94c3a0f..9d9ddea 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -1719,8 +1719,13 @@ evhttp_send_reply_start(struct evhttp_request *req, int code, /* set up to watch for client close */ evhttp_connection_start_detectclose(req->evcon); evhttp_response_code(req, code, reason); - if (req->major == 1 && req->minor == 1) { - /* use chunked encoding for HTTP/1.1 */ + if (evhttp_find_header(req->output_headers, "Content-Length") == NULL + && req->major == 1 && req->minor == 1) { + /* + * prefer HTTP/1.1 chunked encoding to closing the connection; + * note RFC 2616 section 4.4 forbids it with Content-Length: + * and it's not necessary then anyway. + */ evhttp_add_header(req->output_headers, "Transfer-Encoding", "chunked"); req->chunked = 1; -------------- next part -------------- evhttp: return success or failure from evhttp_send_reply_chunk() From: Scott Lamb Callers can use this error return to abort streaming response generation. Signed-off-by: Scott Lamb --- libevent/evhttp.h | 5 ++++- libevent/http-internal.h | 2 +- libevent/http.c | 33 +++++++++++++++++++++++---------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index a47a2f0..349cf95 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -141,7 +141,7 @@ void evhttp_send_reply(struct evhttp_request *req, int code, /* Low-level response interface, for streaming/chunked replies */ void evhttp_send_reply_start(struct evhttp_request *, int, const char *); -void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); +int evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); void evhttp_send_reply_end(struct evhttp_request *); /** @@ -233,6 +233,9 @@ struct evhttp_request *evhttp_request_new( void evhttp_request_set_chunked_cb(struct evhttp_request *, void (*cb)(struct evhttp_request *, void *)); +/** Aborts a request, killing its connection if active. */ +void evhttp_request_abort(struct evhttp_request *req); + /** Frees the request object and removes associated events. */ void evhttp_request_free(struct evhttp_request *req); diff --git a/libevent/http-internal.h b/libevent/http-internal.h index f170865..6aa405f 100644 --- a/libevent/http-internal.h +++ b/libevent/http-internal.h @@ -123,7 +123,7 @@ void evhttp_start_read(struct evhttp_connection *); void evhttp_read_header(evutil_socket_t, short, void *); void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *); -void evhttp_write_buffer(struct evhttp_connection *, +int evhttp_write_buffer(struct evhttp_connection *, void (*)(struct evhttp_connection *, void *), void *); /* response sending HTML the data in the buffer */ diff --git a/libevent/http.c b/libevent/http.c index 9d9ddea..68c1d4b 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -268,7 +268,7 @@ evhttp_method(enum evhttp_cmd_type type) return (method); } -static void +static int evhttp_add_event(struct event *ev, int timeout, int default_timeout) { if (timeout != 0) { @@ -276,18 +276,21 @@ evhttp_add_event(struct event *ev, int timeout, int default_timeout) evutil_timerclear(&tv); tv.tv_sec = timeout != -1 ? timeout : default_timeout; - event_add(ev, &tv); + return (event_add(ev, &tv)); } else { - event_add(ev, NULL); + return (event_add(ev, NULL)); } } -void +int evhttp_write_buffer(struct evhttp_connection *evcon, void (*cb)(struct evhttp_connection *, void *), void *arg) { event_debug(("%s: preparing to write buffer\n", __func__)); + if (evcon->fd == -1) + return (-1); + /* Set call back */ evcon->cb = cb; evcon->cb_arg = arg; @@ -298,7 +301,12 @@ evhttp_write_buffer(struct evhttp_connection *evcon, event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon); EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT); + + if (evhttp_add_event(&evcon->ev, evcon->timeout, + HTTP_WRITE_TIMEOUT) < 0) + return (-1); + + return (0); } /* @@ -1734,15 +1742,17 @@ evhttp_send_reply_start(struct evhttp_request *req, int code, evhttp_write_buffer(req->evcon, NULL, NULL); } -void +int evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf) { if (req->chunked) { - evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", - (unsigned)EVBUFFER_LENGTH(databuf)); + if (evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", + (unsigned)EVBUFFER_LENGTH(databuf)) < 0) + return (-1); } - evbuffer_add_buffer(req->evcon->output_buffer, databuf); - evhttp_write_buffer(req->evcon, NULL, NULL); + if (evbuffer_add_buffer(req->evcon->output_buffer, databuf) < 0) + return (-1); + return (evhttp_write_buffer(req->evcon, NULL, NULL)); } void @@ -2204,6 +2214,9 @@ evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg) void evhttp_request_free(struct evhttp_request *req) { + if (req == NULL) + return; + if (req->remote_host != NULL) event_free(req->remote_host); if (req->uri != NULL) -------------- next part -------------- evhttp: use a 64-bit Content-Length: counter even on 32-bit platforms From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/evhttp.h | 2 +- libevent/http.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index 20a33b9..a47a2f0 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -204,7 +204,7 @@ struct { char *response_code_line; /* Readable response */ struct evbuffer *input_buffer; /* read data */ - int ntoread; + int64_t ntoread; int chunked; struct evbuffer *output_buffer; /* outgoing post or data */ diff --git a/libevent/http.c b/libevent/http.c index 712a899..07d1567 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -707,7 +707,7 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) event_free(p); continue; } - req->ntoread = strtol(p, &endp, 16); + req->ntoread = strtoll(p, &endp, 16); error = *p == '\0' || (*endp != '\0' && *endp != ' '); event_free(p); if (error) { From slamb at slamb.org Sun Feb 10 14:51:21 2008 From: slamb at slamb.org (Scott Lamb) Date: Sun Feb 10 14:51:32 2008 Subject: [Libevent-users] evhttp changes for large requests In-Reply-To: <47AEE13A.6030808@slamb.org> References: <47AEE13A.6030808@slamb.org> Message-ID: <47AF55B9.30402@slamb.org> Scott Lamb wrote: > (1) Use a 64-bit counter for tracking the Content-Length:. Patch > attached. (Somehow this patch isn't working, though? curl complains that > it's stopping at 2^31-1 bytes. I may debug later.) Ahh. My tired eyes missed the most important assignment. Working patch attached. -------------- next part -------------- evhttp: use a 64-bit Content-Length: counter even on 32-bit platforms From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/evhttp.h | 2 +- libevent/http.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index 20a33b9..a47a2f0 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -204,7 +204,7 @@ struct { char *response_code_line; /* Readable response */ struct evbuffer *input_buffer; /* read data */ - int ntoread; + int64_t ntoread; int chunked; struct evbuffer *output_buffer; /* outgoing post or data */ diff --git a/libevent/http.c b/libevent/http.c index 712a899..07d1567 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -707,7 +707,7 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) event_free(p); continue; } - req->ntoread = strtol(p, &endp, 16); + req->ntoread = strtoll(p, &endp, 16); error = *p == '\0' || (*endp != '\0' && *endp != ' '); event_free(p); if (error) { From slamb at slamb.org Sun Feb 10 18:15:06 2008 From: slamb at slamb.org (Scott Lamb) Date: Sun Feb 10 18:15:10 2008 Subject: [Libevent-users] evhttp changes for large requests In-Reply-To: <47AEE13A.6030808@slamb.org> References: <47AEE13A.6030808@slamb.org> Message-ID: <47AF857A.3070508@slamb.org> Scott Lamb wrote: > (4) Detect while streaming that the client connection has been lost, so > that we know to stop sending chunks. Patch attached to return error code > in evhttp_send_reply_chunk(). (Wouldn't hurt to add error returns in > other places, but this is my immediate need.) Hmm, my patch was still appending to the buffer if the client doesn't pay attention; that's probably not ideal either. > (6) Flow control (for the --limit-rate case). I need to keep the > outgoing connection input buffer, outgoing request input buffer, > incoming request output buffer, and incoming connection output buffer > from growing to be ridiculous. I'm considering something like this: > > * define read watermarks on requests; it will only read from the > outgoing connection when the input buffers are below the high (default > of ~0 to preserve existing behavior). it will only call the chunk > callback when the input buffers are above the low (default of 0). > > * define an option to drain the input buffer after calling the chunk > callback, defaulting to 1 for existing behavior. I'd want to turn it off > so I can transfer bytes only if the output buffers are not full. > > * define output watermarks as well. If below low watermark (defaulting > to 0), call a fill function which in my case would also transfer from > input buffers. > > I'm not too attached to this plan. I keep thinking there might be a > simpler way, but I don't see it. Hmm, here's a simpler approach (evhttp-flowcontrol.patch). Opinions? Flow control for evhttp_request * evhttp_request_set_output_watermarks(): - call "fill" when "output buffer size <= low watermark" becomes true - call "drain" when "output buffer size >= high watermark" becomes true * evhttp_request_{pause,resume}(): Pause and resume reading from the socket into the input buffer My client<->proxy request's fill and drain callbacks resume and pause the proxy<->server request, respectively. I'm wondering if there's any point in input watermarks here or for bufferevent either (remove wm_input? there doesn't seem to be an API yet). The idea of the downstream thing toggling it seems simpler. Preliminary version of all patches attached. Still don't have anything for (5). These apply in this order: + evhttp-64bit-length.patch + evhttp-stream-in.patch + evhttp-nonchunked-streaming.patch + evhttp-detect-failure.patch > evhttp-flowcontrol.patch Best regards, Scott -- Scott Lamb -------------- next part -------------- evhttp: stream incoming responses, even if not chunked From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/http.c | 19 ++++++++++++++----- 1 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libevent/http.c b/libevent/http.c index 500a991..d4d0ce6 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -730,11 +730,6 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) EVBUFFER_DATA(buf), req->ntoread); evbuffer_drain(buf, req->ntoread); req->ntoread = -1; - if (req->chunk_cb != NULL) { - (*req->chunk_cb)(req, req->cb_arg); - evbuffer_drain(req->input_buffer, - EVBUFFER_LENGTH(req->input_buffer)); - } } return (0); @@ -768,7 +763,21 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) req->ntoread = 0; evhttp_connection_done(evcon); return; + } else if (req->chunk_cb != NULL) { + /* + * The condition above is simply for efficiency; we do the + * move at the end if we aren't streaming. + */ + req->ntoread -= EVBUFFER_LENGTH(buf); + evbuffer_add_buffer(req->input_buffer, buf); } + + if (EVBUFFER_LENGTH(req->input_buffer) > 0 && req->chunk_cb != NULL) { + (*req->chunk_cb)(req, req->cb_arg); + evbuffer_drain(req->input_buffer, + EVBUFFER_LENGTH(req->input_buffer)); + } + /* Read more! */ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon); EVHTTP_BASE_SET(evcon, &evcon->ev); -------------- next part -------------- evhttp: use a 64-bit Content-Length: counter even on 32-bit platforms From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/evhttp.h | 2 +- libevent/http.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index 20a33b9..a47a2f0 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -204,7 +204,7 @@ struct { char *response_code_line; /* Readable response */ struct evbuffer *input_buffer; /* read data */ - int ntoread; + int64_t ntoread; int chunked; struct evbuffer *output_buffer; /* outgoing post or data */ diff --git a/libevent/http.c b/libevent/http.c index 712a899..500a991 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -707,7 +707,7 @@ evhttp_handle_chunked_read(struct evhttp_request *req, struct evbuffer *buf) event_free(p); continue; } - req->ntoread = strtol(p, &endp, 16); + req->ntoread = strtoll(p, &endp, 16); error = *p == '\0' || (*endp != '\0' && *endp != ' '); event_free(p); if (error) { @@ -1321,7 +1321,7 @@ evhttp_get_body_length(struct evhttp_request *req) req->ntoread = -1; } else { char *endp; - req->ntoread = strtol(content_length, &endp, 10); + req->ntoread = strtoll(content_length, &endp, 10); if (*content_length == '\0' || *endp != '\0') { event_warnx("%s: illegal content length: %s", __func__, content_length); -------------- next part -------------- Flow control for evhttp_request From: Scott Lamb * evhttp_request_set_output_watermarks(): - call "fill" when "output buffer size <= low watermark" becomes true - call "drain" when "output buffer size >= high watermark" becomes true * evhttp_request_{pause,resume}(): Pause and resume reading from the socket into the input buffer Signed-off-by: Scott Lamb --- libevent/evhttp.h | 25 +++++++++++++++++++++++++ libevent/http.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 1 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index 349cf95..ab1ad75 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -139,6 +139,18 @@ void evhttp_send_error(struct evhttp_request *req, int error, void evhttp_send_reply(struct evhttp_request *req, int code, const char *reason, struct evbuffer *databuf); +/** + * Manipulate watermarks for flow control. + * - Calls "fill_cb" with "filldrain_arg" when "output buffer size <= + * low watermark" becomes true. + * - Calls "drain_cb" with "filldrain_arg" when "output buffer size >= + * high watermark" becomes true. + */ +void evhttp_request_set_output_watermarks(struct evhttp_request *, + size_t low_watermark, void (*fill_cb)(struct evhttp_request *, void *), + size_t high_watermark, void (*drain_cb)(struct evhttp_request *, void *), + void *filldrain_arg); + /* Low-level response interface, for streaming/chunked replies */ void evhttp_send_reply_start(struct evhttp_request *, int, const char *); int evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); @@ -183,6 +195,7 @@ struct { #define EVHTTP_REQ_OWN_CONNECTION 0x0001 #define EVHTTP_PROXY_REQUEST 0x0002 #define EVHTTP_USER_OWNED 0x0004 +#define EVHTTP_REQ_PAUSED 0x0008 struct evkeyvalq *input_headers; struct evkeyvalq *output_headers; @@ -208,6 +221,7 @@ struct { int chunked; struct evbuffer *output_buffer; /* outgoing post or data */ + struct event_watermark wm_output; /* Callback */ void (*cb)(struct evhttp_request *, void *); @@ -219,6 +233,14 @@ struct { * the regular callback. */ void (*chunk_cb)(struct evhttp_request *, void *); + + /* + * Fill and drain watermarks - call to maintain consistent + * buffer size. + */ + void (*fill_cb)(struct evhttp_request *, void *); + void (*drain_cb)(struct evhttp_request *, void *); + void *filldrain_arg; }; /** @@ -229,6 +251,9 @@ struct { struct evhttp_request *evhttp_request_new( void (*cb)(struct evhttp_request *, void *), void *arg); +void evhttp_request_pause(struct evhttp_request *); +void evhttp_request_resume(struct evhttp_request *); + /** enable delivery of chunks to requestor */ void evhttp_request_set_chunked_cb(struct evhttp_request *, void (*cb)(struct evhttp_request *, void *)); diff --git a/libevent/http.c b/libevent/http.c index 52913bd..dfe174a 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -606,6 +606,7 @@ void evhttp_write(evutil_socket_t fd, short what, void *arg) { struct evhttp_connection *evcon = arg; + struct evhttp_request* req = TAILQ_FIRST(&evcon->requests); int n; if (what == EV_TIMEOUT) { @@ -626,6 +627,10 @@ evhttp_write(evutil_socket_t fd, short what, void *arg) return; } + if (EVBUFFER_LENGTH(req->evcon->output_buffer) <= + req->wm_output.low && req->fill_cb != NULL) + req->fill_cb(req, req->filldrain_arg); + if (EVBUFFER_LENGTH(evcon->output_buffer) != 0) { evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT); @@ -789,7 +794,8 @@ evhttp_read_body(struct evhttp_connection *evcon, struct evhttp_request *req) /* Read more! */ event_set(&evcon->ev, evcon->fd, EV_READ, evhttp_read, evcon); EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT); + if ((req->flags & EVHTTP_REQ_PAUSED) == 0) + evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT); } /* @@ -1755,6 +1761,11 @@ evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf) } if (evbuffer_add_buffer(req->evcon->output_buffer, databuf) < 0) return (-1); + + if (EVBUFFER_LENGTH(req->evcon->output_buffer) >= + req->wm_output.high && req->drain_cb != NULL) + req->drain_cb(req, req->filldrain_arg); + return (evhttp_write_buffer(req->evcon, NULL, NULL)); } @@ -2255,12 +2266,53 @@ evhttp_request_is_owned(struct evhttp_request *req) } void +evhttp_request_pause(struct evhttp_request *req) +{ + req->flags |= EVHTTP_REQ_PAUSED; +} + +void +evhttp_request_resume(struct evhttp_request *req) +{ + struct evhttp_connection *evcon = req->evcon; + + if (req->evcon != NULL && (req->flags & EVHTTP_REQ_PAUSED) != 0) + evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_READ_TIMEOUT); + req->flags &= ~EVHTTP_REQ_PAUSED; + +} + +void evhttp_request_set_chunked_cb(struct evhttp_request *req, void (*cb)(struct evhttp_request *, void *)) { req->chunk_cb = cb; } +void +evhttp_request_set_output_watermarks(struct evhttp_request *req, + size_t low_watermark, void (*fill_cb)(struct evhttp_request *, void *), + size_t high_watermark, void (*drain_cb)(struct evhttp_request *, void *), + void *filldrain_arg) +{ + req->wm_output.low = low_watermark; + req->wm_output.high = high_watermark; + req->fill_cb = fill_cb; + req->drain_cb = drain_cb; + req->filldrain_arg = filldrain_arg; + + if (req->evcon && req->evcon->output_buffer) { + /* See if we need to call them immediately. */ + size_t len = EVBUFFER_LENGTH(req->evcon->output_buffer); + + if (len <= low_watermark) + fill_cb(req, filldrain_arg); + if (len >= high_watermark) + drain_cb(req, filldrain_arg); + } +} + + /* * Allows for inspection of the request URI */ -------------- next part -------------- evhttp: stream large responses without requiring chunking From: Scott Lamb Signed-off-by: Scott Lamb --- libevent/http.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libevent/http.c b/libevent/http.c index d4d0ce6..07426b9 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -1719,8 +1719,13 @@ evhttp_send_reply_start(struct evhttp_request *req, int code, /* set up to watch for client close */ evhttp_connection_start_detectclose(req->evcon); evhttp_response_code(req, code, reason); - if (req->major == 1 && req->minor == 1) { - /* use chunked encoding for HTTP/1.1 */ + if (evhttp_find_header(req->output_headers, "Content-Length") == NULL + && req->major == 1 && req->minor == 1) { + /* + * prefer HTTP/1.1 chunked encoding to closing the connection; + * note RFC 2616 section 4.4 forbids it with Content-Length: + * and it's not necessary then anyway. + */ evhttp_add_header(req->output_headers, "Transfer-Encoding", "chunked"); req->chunked = 1; -------------- next part -------------- evhttp: return success or failure from evhttp_send_reply_chunk() From: Scott Lamb Callers can use this error return to abort streaming response generation. Signed-off-by: Scott Lamb --- libevent/evhttp.h | 5 ++++- libevent/http-internal.h | 2 +- libevent/http.c | 36 ++++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/libevent/evhttp.h b/libevent/evhttp.h index a47a2f0..349cf95 100644 --- a/libevent/evhttp.h +++ b/libevent/evhttp.h @@ -141,7 +141,7 @@ void evhttp_send_reply(struct evhttp_request *req, int code, /* Low-level response interface, for streaming/chunked replies */ void evhttp_send_reply_start(struct evhttp_request *, int, const char *); -void evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); +int evhttp_send_reply_chunk(struct evhttp_request *, struct evbuffer *); void evhttp_send_reply_end(struct evhttp_request *); /** @@ -233,6 +233,9 @@ struct evhttp_request *evhttp_request_new( void evhttp_request_set_chunked_cb(struct evhttp_request *, void (*cb)(struct evhttp_request *, void *)); +/** Aborts a request, killing its connection if active. */ +void evhttp_request_abort(struct evhttp_request *req); + /** Frees the request object and removes associated events. */ void evhttp_request_free(struct evhttp_request *req); diff --git a/libevent/http-internal.h b/libevent/http-internal.h index f170865..6aa405f 100644 --- a/libevent/http-internal.h +++ b/libevent/http-internal.h @@ -123,7 +123,7 @@ void evhttp_start_read(struct evhttp_connection *); void evhttp_read_header(evutil_socket_t, short, void *); void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *); -void evhttp_write_buffer(struct evhttp_connection *, +int evhttp_write_buffer(struct evhttp_connection *, void (*)(struct evhttp_connection *, void *), void *); /* response sending HTML the data in the buffer */ diff --git a/libevent/http.c b/libevent/http.c index 07426b9..52913bd 100644 --- a/libevent/http.c +++ b/libevent/http.c @@ -268,7 +268,7 @@ evhttp_method(enum evhttp_cmd_type type) return (method); } -static void +static int evhttp_add_event(struct event *ev, int timeout, int default_timeout) { if (timeout != 0) { @@ -276,18 +276,21 @@ evhttp_add_event(struct event *ev, int timeout, int default_timeout) evutil_timerclear(&tv); tv.tv_sec = timeout != -1 ? timeout : default_timeout; - event_add(ev, &tv); + return (event_add(ev, &tv)); } else { - event_add(ev, NULL); + return (event_add(ev, NULL)); } } -void +int evhttp_write_buffer(struct evhttp_connection *evcon, void (*cb)(struct evhttp_connection *, void *), void *arg) { event_debug(("%s: preparing to write buffer\n", __func__)); + if (evcon->fd == -1) + return (-1); + /* Set call back */ evcon->cb = cb; evcon->cb_arg = arg; @@ -298,7 +301,12 @@ evhttp_write_buffer(struct evhttp_connection *evcon, event_set(&evcon->ev, evcon->fd, EV_WRITE, evhttp_write, evcon); EVHTTP_BASE_SET(evcon, &evcon->ev); - evhttp_add_event(&evcon->ev, evcon->timeout, HTTP_WRITE_TIMEOUT); + + if (evhttp_add_event(&evcon->ev, evcon->timeout, + HTTP_WRITE_TIMEOUT) < 0) + return (-1); + + return (0); } /* @@ -1734,15 +1742,20 @@ evhttp_send_reply_start(struct evhttp_request *req, int code, evhttp_write_buffer(req->evcon, NULL, NULL); } -void +int evhttp_send_reply_chunk(struct evhttp_request *req, struct evbuffer *databuf) { + if (req->evcon->state == EVCON_DISCONNECTED) + return (-1); + if (req->chunked) { - evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", - (unsigned)EVBUFFER_LENGTH(databuf)); + if (evbuffer_add_printf(req->evcon->output_buffer, "%x\r\n", + (unsigned)EVBUFFER_LENGTH(databuf)) < 0) + return (-1); } - evbuffer_add_buffer(req->evcon->output_buffer, databuf); - evhttp_write_buffer(req->evcon, NULL, NULL); + if (evbuffer_add_buffer(req->evcon->output_buffer, databuf) < 0) + return (-1); + return (evhttp_write_buffer(req->evcon, NULL, NULL)); } void @@ -2204,6 +2217,9 @@ evhttp_request_new(void (*cb)(struct evhttp_request *, void *), void *arg) void evhttp_request_free(struct evhttp_request *req) { + if (req == NULL) + return; + if (req->remote_host != NULL) event_free(req->remote_host); if (req->uri != NULL) From springandeulv at gmail.com Mon Feb 11 13:10:29 2008 From: springandeulv at gmail.com (Springande Ulv) Date: Mon Feb 11 13:10:43 2008 Subject: [Libevent-users] bug: handler called again after event_del() Message-ID: <55D83310-BA26-40F9-8A3F-9C221EBA82AC@gmail.com> I'm using libevent-1.4.1-beta on Mac OS X Leopard. Libevent is using kqueue. I find that libevent call my event handler several times when it should only call the handler once. This is the case if I register for a one shoot event notification as shown below or if I use EV_READ| EV_PERSIST and explicit remove the event using event_del(event) event_set(event, fd, EV_READ, handler, arg); event_base_set(eventbase, event) event_priority_set(event, priority) event_add(event, timeout) In both cases I see spurious calls to the event handler which should not occur. Has anyone else seen this problem on Mac OS X? Otherwise I'll try to debug more and come up with more specifics. Also I also see that libevent seems to enter a while-true loop, grab all CPU and freeze during handling of a write|timeout. I have a feeling this is related to the spurious kevent notification mentioned above. Has anyone seen this as well? From slamb at slamb.org Mon Feb 11 15:29:00 2008 From: slamb at slamb.org (Scott Lamb) Date: Mon Feb 11 15:30:18 2008 Subject: [Libevent-users] bug: handler called again after event_del() In-Reply-To: <55D83310-BA26-40F9-8A3F-9C221EBA82AC@gmail.com> References: <55D83310-BA26-40F9-8A3F-9C221EBA82AC@gmail.com> Message-ID: <47B0B00C.4080205@slamb.org> Springande Ulv wrote: > Has anyone else seen this problem on Mac OS X? Otherwise I'll try to > debug more and come up with more specifics. > > Also I also see that libevent seems to enter a while-true loop, grab all > CPU and freeze during handling of a write|timeout. I have a feeling this > is related to the spurious kevent notification mentioned above. Has > anyone seen this as well?_______________________________________________ libevent trunk head works fine for me with kqueue on OS X 10.4. I haven't tried libevent 1.4.1-beta or OS X 10.5 yet. What does test/test.sh say? Best regards, Scott From provos at citi.umich.edu Tue Feb 12 01:02:59 2008 From: provos at citi.umich.edu (Niels Provos) Date: Tue Feb 12 01:03:09 2008 Subject: [Libevent-users] [PATCH] evport fix to handle EAGAIN In-Reply-To: <47AC0B5D.1040108@nlnetlabs.nl> References: <47A990DF.9050004@nlnetlabs.nl> <850f7cbe0802060815x6b555051yed99ba7e9fc8e43a@mail.gmail.com> <47AABE32.1020503@nlnetlabs.nl> <850f7cbe0802070751l438da1ccr66dc6f2d1db0dc9c@mail.gmail.com> <47AC0B5D.1040108@nlnetlabs.nl> Message-ID: <850f7cbe0802112202o91cc55oe7021eff1f0643d5@mail.gmail.com> On Feb 7, 2008 11:57 PM, Wouter Wijngaards wrote: > To be sure, below is what I did. > > Best regards, > ~ Wouter > > + evport_dealloc, > + 1 /* need reinit */ > > + devpoll_dealloc, > + 1 /* need reinit */ I submitted this to both trunk and patches-1.4 Thank you, Niels. From springandeulv at gmail.com Tue Feb 12 10:26:32 2008 From: springandeulv at gmail.com (Springande Ulv) Date: Tue Feb 12 10:27:01 2008 Subject: [Libevent-users] bug: handler called again after event_del() In-Reply-To: <47B0B00C.4080205@slamb.org> References: <55D83310-BA26-40F9-8A3F-9C221EBA82AC@gmail.com> <47B0B00C.4080205@slamb.org> Message-ID: <1D4BC025-924F-4807-A23C-DB0E1F393979@gmail.com> My bad, had a timeout which I did not see coming. On 11. feb.. 2008, at 21.29, Scott Lamb wrote: > Springande Ulv wrote: >> Has anyone else seen this problem on Mac OS X? Otherwise I'll try >> to debug more and come up with more specifics. >> Also I also see that libevent seems to enter a while-true loop, >> grab all CPU and freeze during handling of a write|timeout. I have >> a feeling this is related to the spurious kevent notification >> mentioned above. Has anyone seen this as well? >> _______________________________________________ > > libevent trunk head works fine for me with kqueue on OS X 10.4. I > haven't tried libevent 1.4.1-beta or OS X 10.5 yet. > > What does test/test.sh say? From springandeulv at gmail.com Wed Feb 13 14:32:31 2008 From: springandeulv at gmail.com (Springande Ulv) Date: Wed Feb 13 14:33:06 2008 Subject: [Libevent-users] event_base_get_method patch Message-ID: To be able to obtain the event method used if libevent was initialized with event_base_new() a new method is needed since event_get_method() operates on the current base, which is then invalid and since event_base is an opaque struct for clients of the library. This small patch add just such a method, event_base_get_method() -------------- next part -------------- A non-text attachment was scrubbed... Name: method.patch Type: application/octet-stream Size: 1028 bytes Desc: not available Url : http://monkeymail.org/archives/libevent-users/attachments/20080213/1e86e6c3/method.obj -------------- next part -------------- From vsevolod at highsecure.ru Fri Feb 15 11:22:34 2008 From: vsevolod at highsecure.ru (Vsevolod Stakhov) Date: Fri Feb 15 11:22:34 2008 Subject: [Libevent-users] [patch] replace linear search in evdns with hashed list and add support of listening on unix sockets for evhttp Message-ID: <47B5BC4A.3010905@highsecure.ru> Hi, We found a problem with list requests_inflight while using evdns on highly loaded system (about 2000 DNS requests per second). First of all linear search is slow and second is that this list is not big enough to handle all requests, so many requests were in requests_waiting list, that has no limits on length. Due to this application grows in size constantly and is not able to serve requests. I've replaced linear list with hashed list, that improves performance. The one thing that is not logical (but I don't know how to make it better) is that I'm using bzero of hash nests array in reading resolv conf function. The second part of patch allows using evhttp module for listening on unix sockets. This is our project specific requirement, but it may be useful for others. -------------- next part -------------- --- a/evdns.c Wed Feb 13 15:31:39 2008 +0300 +++ b/evdns.c Wed Feb 13 16:24:17 2008 +0300 @@ -156,6 +156,7 @@ typedef unsigned int uint; #endif #define MAX_ADDRS 32 /* maximum number of addresses from a single packet */ +#define MAX_NESTS 1024 /* numbers of nests in hashed list of pending requests */ /* which we bother recording */ #define TYPE_A EVDNS_TYPE_A @@ -231,7 +232,7 @@ struct nameserver { char write_waiting; /* true if we are waiting for EV_WRITE events */ }; -static struct request *req_head = NULL, *req_waiting_head = NULL; +static struct request *req_head[MAX_NESTS], *req_waiting_head = NULL; static struct nameserver *server_head = NULL; /* Represents a local port where we're listening for DNS requests. Right now, */ @@ -310,7 +311,7 @@ static int global_requests_inflight = 0; /* and are counted here */ static int global_requests_waiting = 0; -static int global_max_requests_inflight = 64; +static int global_max_requests_inflight = 2048; static struct timeval global_timeout = {5, 0}; /* 5 seconds */ static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ @@ -443,7 +444,7 @@ _evdns_log(int warn, const char *fmt, .. /* failure */ static struct request * request_find_from_trans_id(u16 trans_id) { - struct request *req = req_head, *const started_at = req_head; + struct request *req = req_head[trans_id % MAX_NESTS], *const started_at = req_head[trans_id % MAX_NESTS]; if (req) { do { @@ -498,6 +499,7 @@ static void static void nameserver_failed(struct nameserver *const ns, const char *msg) { struct request *req, *started_at; + int i; /* if this nameserver has already been marked as failed */ /* then don't do anything */ if (!ns->state) return; @@ -529,17 +531,19 @@ nameserver_failed(struct nameserver *con /* trying to reassign requests to one */ if (!global_good_nameservers) return; - req = req_head; - started_at = req_head; - if (req) { - do { - if (req->tx_count == 0 && req->ns == ns) { - /* still waiting to go out, can be moved */ - /* to another server */ - req->ns = nameserver_pick(); - } - req = req->next; - } while (req != started_at); + for (i = 0; i < MAX_NESTS; i ++) { + req = req_head[i]; + started_at = req_head[i]; + if (req) { + do { + if (req->tx_count == 0 && req->ns == ns) { + /* still waiting to go out, can be moved */ + /* to another server */ + req->ns = nameserver_pick(); + } + req = req->next; + } while (req != started_at); + } } } @@ -650,7 +654,7 @@ evdns_requests_pump_waiting_queue(void) req->ns = nameserver_pick(); request_trans_id_set(req, transaction_id_pick()); - evdns_request_insert(req, &req_head); + evdns_request_insert(req, &req_head[req->trans_id % MAX_NESTS]); evdns_request_transmit(req); evdns_transmit(); } @@ -742,19 +746,19 @@ reply_handle(struct request *const req, /* a new request was issued so this request is finished and */ /* the user callback will be made when that request (or a */ /* child of it) finishes. */ - request_finished(req, &req_head); + request_finished(req, &req_head[req->trans_id % MAX_NESTS]); return; } } /* all else failed. Pass the failure up */ reply_callback(req, 0, error, NULL); - request_finished(req, &req_head); + request_finished(req, &req_head[req->trans_id % MAX_NESTS]); } else { /* all ok, tell the user */ reply_callback(req, ttl, 0, reply); nameserver_up(req->ns); - request_finished(req, &req_head); + request_finished(req, &req_head[req->trans_id % MAX_NESTS]); } } @@ -1090,12 +1094,13 @@ static u16 static u16 transaction_id_pick(void) { for (;;) { - const struct request *req = req_head, *started_at; + const struct request *req, *started_at; u16 trans_id = trans_id_function(); + req = req_head[trans_id % MAX_NESTS]; if (trans_id == 0xffff) continue; /* now check to see if that id is already inflight */ - req = started_at = req_head; + req = started_at = req_head[trans_id % MAX_NESTS]; if (req) { do { if (req->trans_id == trans_id) break; @@ -1869,7 +1874,7 @@ evdns_request_timeout_callback(int fd, s if (req->tx_count >= global_max_retransmits) { /* this request has failed */ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); - request_finished(req, &req_head); + request_finished(req, &req_head[req->trans_id % MAX_NESTS]); } else { /* retransmit it */ evdns_request_transmit(req); @@ -1981,19 +1986,22 @@ nameserver_send_probe(struct nameserver /* 1 tried to transmit something */ static int evdns_transmit(void) { + int i; char did_try_to_transmit = 0; - - if (req_head) { - struct request *const started_at = req_head, *req = req_head; - /* first transmit all the requests which are currently waiting */ - do { - if (req->transmit_me) { - did_try_to_transmit = 1; - evdns_request_transmit(req); - } - - req = req->next; - } while (req != started_at); + + for (i = 0; i < MAX_NESTS; i ++) { + if (req_head[i]) { + struct request *const started_at = req_head[i], *req = req_head[i]; + /* first transmit all the requests which are currently waiting */ + do { + if (req->transmit_me) { + did_try_to_transmit = 1; + evdns_request_transmit(req); + } + + req = req->next; + } while (req != started_at); + } } return did_try_to_transmit; @@ -2019,7 +2027,8 @@ evdns_clear_nameservers_and_suspend(void evdns_clear_nameservers_and_suspend(void) { struct nameserver *server = server_head, *started_at = server_head; - struct request *req = req_head, *req_started_at = req_head; + struct request *req, *req_started_at; + int i; if (!server) return 0; @@ -2036,29 +2045,35 @@ evdns_clear_nameservers_and_suspend(void } server_head = NULL; global_good_nameservers = 0; - - while (req) { - struct request *next = req->next; - req->tx_count = req->reissue_count = 0; - req->ns = NULL; - /* ???? What to do about searches? */ - (void) evtimer_del(&req->timeout_event); - req->trans_id = 0; - req->transmit_me = 0; - - global_requests_waiting++; - evdns_request_insert(req, &req_waiting_head); - /* We want to insert these suspended elements at the front of - * the waiting queue, since they were pending before any of - * the waiting entries were added. This is a circular list, - * so we can just shift the start back by one.*/ - req_waiting_head = req_waiting_head->prev; - - if (next == req_started_at) - break; - req = next; - } - req_head = NULL; + + for (i = 0; i < MAX_NESTS; i ++) { + req = req_head[i]; + req_started_at = req; + + while (req) { + struct request *next = req->next; + req->tx_count = req->reissue_count = 0; + req->ns = NULL; + /* ???? What to do about searches? */ + (void) evtimer_del(&req->timeout_event); + req->trans_id = 0; + req->transmit_me = 0; + + global_requests_waiting++; + evdns_request_insert(req, &req_waiting_head); + /* We want to insert these suspended elements at the front of + * the waiting queue, since they were pending before any of + * the waiting entries were added. This is a circular list, + * so we can just shift the start back by one.*/ + req_waiting_head = req_waiting_head->prev; + + if (next == req_started_at) + break; + req = next; + } + req_head[i] = NULL; + } + global_requests_inflight = 0; return 0; @@ -2245,7 +2260,7 @@ request_submit(struct request *const req if (req->ns) { /* if it has a nameserver assigned then this is going */ /* straight into the inflight queue */ - evdns_request_insert(req, &req_head); + evdns_request_insert(req, &req_head[req->trans_id % MAX_NESTS]); global_requests_inflight++; evdns_request_transmit(req); } else { @@ -2714,6 +2729,9 @@ evdns_resolv_conf_parse(int flags, const u8 *resolv; char *start; int err = 0; + + /* XXX: remove this hack from here */ + bzero (req_head, MAX_NESTS * sizeof (req_head[0])); log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); @@ -3002,11 +3020,14 @@ evdns_shutdown(int fail_requests) { struct nameserver *server, *server_next; struct search_domain *dom, *dom_next; - - while (req_head) { - if (fail_requests) - reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_head, &req_head); + int i; + + for (i = 0; i < MAX_NESTS; i ++) { + while (req_head[i]) { + if (fail_requests) + reply_callback(req_head[i], 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(req_head[i], &req_head[i]); + } } while (req_waiting_head) { if (fail_requests) --- a/http.c Wed Feb 13 15:31:39 2008 +0300 +++ b/http.c Wed Feb 13 16:24:17 2008 +0300 @@ -78,6 +78,9 @@ #ifdef HAVE_FCNTL_H #include #endif +#ifndef WIN32 +#include +#endif #undef timeout_pending #undef timeout_initialized @@ -2349,18 +2352,25 @@ name_from_addr(struct sockaddr *sa, sock static char ntop[NI_MAXHOST]; static char strport[NI_MAXSERV]; int ni_result; - - if ((ni_result = getnameinfo(sa, salen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { - if (ni_result == EAI_SYSTEM) - event_err(1, "getnameinfo failed"); - else - event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); - } - + + if (sa->sa_family == PF_UNIX) { + strncpy (ntop, ((struct sockaddr_un *)sa)->sun_path, NI_MAXHOST - 1); + strport[0] = '0'; + strport[1] = '\0'; + } + else { + if ((ni_result = getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + if (ni_result == EAI_SYSTEM) + event_err(1, "getnameinfo failed"); + else + event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); + } + } *phost = ntop; *pport = strport; + #else /* XXXX */ #endif From provos at citi.umich.edu Sat Feb 16 01:11:11 2008 From: provos at citi.umich.edu (Niels Provos) Date: Sat Feb 16 01:11:23 2008 Subject: [Libevent-users] event_base_get_method patch In-Reply-To: References: Message-ID: <850f7cbe0802152211h3cf8011dv48777ee8748428a3@mail.gmail.com> Thank you. I applied this to both trunk and patches-1.4. Niels. On Feb 13, 2008 11:32 AM, Springande Ulv wrote: > To be able to obtain the event method used if libevent was initialized > with event_base_new() a new method is needed since event_get_method() > operates on the current base, which is then invalid and since > event_base is an opaque struct for clients of the library. This small > patch add just such a method, event_base_get_method() > > > > > _______________________________________________ > Libevent-users mailing list > Libevent-users@monkey.org > http://monkeymail.org/mailman/listinfo/libevent-users > > From nickm at freehaven.net Sat Feb 16 12:26:30 2008 From: nickm at freehaven.net (nickm@freehaven.net) Date: Sat Feb 16 12:27:16 2008 Subject: [Libevent-users] [patch] replace linear search in evdns with hashed list and add support of listening on unix sockets for evhttp In-Reply-To: <47B5BC4A.3010905@highsecure.ru> References: <47B5BC4A.3010905@highsecure.ru> Message-ID: <20080216172630.GC21803@moria.seul.org> On Fri, Feb 15, 2008 at 07:22:34PM +0300, Vsevolod Stakhov wrote: > Hi, > > We found a problem with list requests_inflight while using evdns on > highly loaded system (about 2000 DNS requests per second). First of all > linear search is slow and second is that this list is not big enough to > handle all requests, so many requests were in requests_waiting list, > that has no limits on length. Due to this application grows in size > constantly and is not able to serve requests. I've replaced linear list > with hashed list, that improves performance. Hi! This looks pretty good. I'd like to apply it for trunk to apply it to the next release after 1.4. (I want to avoid applying anything but straight bugfixes to 1.4 at this point, so that it can get out of beta soon.) A few comments and questions: - Instead of changing global_max_request_inflight in the code manually, you can just call evdns_set_option("max-inflight:", "2048", DNS_OPTIONS_ALL). - Do you have profileing or benchmarking numbers to show how much this helps? I'd like to be able to confirm that this helps, that "2048" is a good value for MAX_NESTS, etc. - It looks like we walk the whole inflight list on nameserver_failed(), evdns_transmit(), evdns_clear_nameservers_and_suspend(), and evdns_shutdown(). Of these, evdns_transmit() still worries me a little: unlike the others, it is frequently called during non-exceptional conditions. Does it show up in your profiles? > The one thing that is not > logical (but I don't know how to make it better) is that I'm using bzero > of hash nests array in reading resolv conf function. Probably it's better to do this in some function called from both evdns_resolv_conf_parse() and evdns_config_windows_nameservers(). It should probably use a flag to make sure that it hasn't already zeroed the array. Also, bzero() is deprecated in POSIX.1; you should probably use memset() instead. Please let me know what you think; I'd like to get a feature like this into libevent. yrs, -- Nick From vsevolod at highsecure.ru Mon Feb 18 07:58:09 2008 From: vsevolod at highsecure.ru (Vsevolod Stakhov) Date: Mon Feb 18 07:58:12 2008 Subject: [Libevent-users] [patch] replace linear search in evdns with hashed list and add support of listening on unix sockets for evhttp In-Reply-To: <20080216172630.GC21803@moria.seul.org> References: <47B5BC4A.3010905@highsecure.ru> <20080216172630.GC21803@moria.seul.org> Message-ID: <47B980E1.60006@highsecure.ru> nickm@freehaven.net wrote: > On Fri, Feb 15, 2008 at 07:22:34PM +0300, Vsevolod Stakhov wrote: >> Hi, >> >> We found a problem with list requests_inflight while using evdns on >> highly loaded system (about 2000 DNS requests per second). First of all >> linear search is slow and second is that this list is not big enough to >> handle all requests, so many requests were in requests_waiting list, >> that has no limits on length. Due to this application grows in size >> constantly and is not able to serve requests. I've replaced linear list >> with hashed list, that improves performance. > > Hi! This looks pretty good. I'd like to apply it for trunk to apply > it to the next release after 1.4. (I want to avoid applying anything > but straight bugfixes to 1.4 at this point, so that it can get out of > beta soon.) A few comments and questions: > > - Instead of changing global_max_request_inflight in the code manually, > you can just call > evdns_set_option("max-inflight:", "2048", DNS_OPTIONS_ALL). Yes, we are using this method, but setting big value for this was a real pain in case of linear search. I've just changed this value for our default, but it is not useful for the most cases (not so much load). > - Do you have profileing or benchmarking numbers to show how much this > helps? I'd like to be able to confirm that this helps, that "2048" is > a good value for MAX_NESTS, etc. Maybe this value should be calculated as global_max_request_inflighs/10 to have not more than 10 items in each nest. > - It looks like we walk the whole inflight list on nameserver_failed(), > evdns_transmit(), evdns_clear_nameservers_and_suspend(), and > evdns_shutdown(). Of these, evdns_transmit() still worries me a > little: unlike the others, it is frequently called during > non-exceptional conditions. Does it show up in your profiles? Well, walking through the whole inflight list in case of hashed list is not much harder than walking through linear list. If MAX_NESTS would be global variable, calculated from global_max_request_inflighs it would not be pain at all for most cases. >> The one thing that is not >> logical (but I don't know how to make it better) is that I'm using bzero >> of hash nests array in reading resolv conf function. > > Probably it's better to do this in some function called from both > evdns_resolv_conf_parse() and evdns_config_windows_nameservers(). It > should probably use a flag to make sure that it hasn't already zeroed the > array. > > Also, bzero() is deprecated in POSIX.1; you should probably use memset() > instead. Agreed. From vsevolod at highsecure.ru Mon Feb 18 10:56:16 2008 From: vsevolod at highsecure.ru (Vsevolod Stakhov) Date: Mon Feb 18 10:56:25 2008 Subject: [Libevent-users] [patch] replace linear search in evdns with hashed list and add support of listening on unix sockets for evhttp In-Reply-To: <47B980E1.60006@highsecure.ru> References: <47B5BC4A.3010905@highsecure.ru> <20080216172630.GC21803@moria.seul.org> <47B980E1.60006@highsecure.ru> Message-ID: <47B9AAA0.8040706@highsecure.ru> Vsevolod Stakhov wrote: > nickm@freehaven.net wrote: >> On Fri, Feb 15, 2008 at 07:22:34PM +0300, Vsevolod Stakhov wrote: >>> Hi, >>> >>> We found a problem with list requests_inflight while using evdns on >>> highly loaded system (about 2000 DNS requests per second). First of >>> all linear search is slow and second is that this list is not big >>> enough to handle all requests, so many requests were in >>> requests_waiting list, that has no limits on length. Due to this >>> application grows in size constantly and is not able to serve >>> requests. I've replaced linear list with hashed list, that improves >>> performance. >> >> Hi! This looks pretty good. I'd like to apply it for trunk to apply >> it to the next release after 1.4. (I want to avoid applying anything >> but straight bugfixes to 1.4 at this point, so that it can get out of >> beta soon.) A few comments and questions: >> ... I've made some modifications to patch to attract your wishes, thought I've not tested it in production yet. -------------- next part -------------- diff -r 2609c564f854 -r d3e5ec3459f9 evdns.c --- a/evdns.c Mon Feb 18 18:55:03 2008 +0300 +++ b/evdns.c Wed Feb 13 15:31:39 2008 +0300 @@ -231,7 +231,7 @@ struct nameserver { char write_waiting; /* true if we are waiting for EV_WRITE events */ }; -static struct request **req_head = NULL, *req_waiting_head = NULL; +static struct request *req_head = NULL, *req_waiting_head = NULL; static struct nameserver *server_head = NULL; /* Represents a local port where we're listening for DNS requests. Right now, */ @@ -310,9 +310,7 @@ static int global_requests_inflight = 0; /* and are counted here */ static int global_requests_waiting = 0; -static int global_max_requests_inflight = 1024; -/* numbers of nests in hashed list of pending requests */ -static int global_max_nests = 128; +static int global_max_requests_inflight = 64; static struct timeval global_timeout = {5, 0}; /* 5 seconds */ static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ @@ -445,16 +443,13 @@ _evdns_log(int warn, const char *fmt, .. /* failure */ static struct request * request_find_from_trans_id(u16 trans_id) { - struct request *req, *started_at; - if (req_head) { - req = req_head[trans_id % global_max_nests]; - started_at = req_head[trans_id % global_max_nests]; - if (req) { - do { - if (req->trans_id == trans_id) return req; - req = req->next; - } while (req != started_at); - } + struct request *req = req_head, *const started_at = req_head; + + if (req) { + do { + if (req->trans_id == trans_id) return req; + req = req->next; + } while (req != started_at); } return NULL; @@ -503,7 +498,6 @@ static void static void nameserver_failed(struct nameserver *const ns, const char *msg) { struct request *req, *started_at; - int i; /* if this nameserver has already been marked as failed */ /* then don't do anything */ if (!ns->state) return; @@ -533,21 +527,19 @@ nameserver_failed(struct nameserver *con /* if we don't have *any* good nameservers then there's no point */ /* trying to reassign requests to one */ - if (!global_good_nameservers || !req_head) return; - - for (i = 0; i < global_max_nests; i ++) { - req = req_head[i]; - started_at = req_head[i]; - if (req) { - do { - if (req->tx_count == 0 && req->ns == ns) { - /* still waiting to go out, can be moved */ - /* to another server */ - req->ns = nameserver_pick(); - } - req = req->next; - } while (req != started_at); - } + if (!global_good_nameservers) return; + + req = req_head; + started_at = req_head; + if (req) { + do { + if (req->tx_count == 0 && req->ns == ns) { + /* still waiting to go out, can be moved */ + /* to another server */ + req->ns = nameserver_pick(); + } + req = req->next; + } while (req != started_at); } } @@ -657,10 +649,8 @@ evdns_requests_pump_waiting_queue(void) req->ns = nameserver_pick(); request_trans_id_set(req, transaction_id_pick()); - - if (req_head) { - evdns_request_insert(req, &req_head[req->trans_id % global_max_nests]); - } + + evdns_request_insert(req, &req_head); evdns_request_transmit(req); evdns_transmit(); } @@ -752,25 +742,19 @@ reply_handle(struct request *const req, /* a new request was issued so this request is finished and */ /* the user callback will be made when that request (or a */ /* child of it) finishes. */ - if (req_head) { - request_finished(req, &req_head[req->trans_id % global_max_nests]); - } + request_finished(req, &req_head); return; } } /* all else failed. Pass the failure up */ reply_callback(req, 0, error, NULL); - if (req_head) { - request_finished(req, &req_head[req->trans_id % global_max_nests]); - } + request_finished(req, &req_head); } else { /* all ok, tell the user */ reply_callback(req, ttl, 0, reply); nameserver_up(req->ns); - if (req_head) { - request_finished(req, &req_head[req->trans_id % global_max_nests]); - } + request_finished(req, &req_head); } } @@ -1106,14 +1090,12 @@ static u16 static u16 transaction_id_pick(void) { for (;;) { - const struct request *req = NULL, *started_at = NULL; + const struct request *req = req_head, *started_at; u16 trans_id = trans_id_function(); if (trans_id == 0xffff) continue; /* now check to see if that id is already inflight */ - if (req_head) { - req = started_at = req_head[trans_id % global_max_nests]; - } + req = started_at = req_head; if (req) { do { if (req->trans_id == trans_id) break; @@ -1887,9 +1869,7 @@ evdns_request_timeout_callback(int fd, s if (req->tx_count >= global_max_retransmits) { /* this request has failed */ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); - if (req_head) { - request_finished(req, &req_head[req->trans_id % global_max_nests]); - } + request_finished(req, &req_head); } else { /* retransmit it */ evdns_request_transmit(req); @@ -2001,22 +1981,19 @@ nameserver_send_probe(struct nameserver /* 1 tried to transmit something */ static int evdns_transmit(void) { - int i; char did_try_to_transmit = 0; - - for (i = 0; i < global_max_nests; i ++) { - if (req_head && req_head[i]) { - struct request *const started_at = req_head[i], *req = req_head[i]; - /* first transmit all the requests which are currently waiting */ - do { - if (req->transmit_me) { - did_try_to_transmit = 1; - evdns_request_transmit(req); - } - - req = req->next; - } while (req != started_at); - } + + if (req_head) { + struct request *const started_at = req_head, *req = req_head; + /* first transmit all the requests which are currently waiting */ + do { + if (req->transmit_me) { + did_try_to_transmit = 1; + evdns_request_transmit(req); + } + + req = req->next; + } while (req != started_at); } return did_try_to_transmit; @@ -2042,8 +2019,7 @@ evdns_clear_nameservers_and_suspend(void evdns_clear_nameservers_and_suspend(void) { struct nameserver *server = server_head, *started_at = server_head; - struct request *req, *req_started_at; - int i; + struct request *req = req_head, *req_started_at = req_head; if (!server) return 0; @@ -2060,37 +2036,29 @@ evdns_clear_nameservers_and_suspend(void } server_head = NULL; global_good_nameservers = 0; - - if (req_head) { - for (i = 0; i < global_max_nests; i ++) { - req = req_head[i]; - req_started_at = req; - - while (req) { - struct request *next = req->next; - req->tx_count = req->reissue_count = 0; - req->ns = NULL; - /* ???? What to do about searches? */ - (void) evtimer_del(&req->timeout_event); - req->trans_id = 0; - req->transmit_me = 0; - - global_requests_waiting++; - evdns_request_insert(req, &req_waiting_head); - /* We want to insert these suspended elements at the front of - * the waiting queue, since they were pending before any of - * the waiting entries were added. This is a circular list, - * so we can just shift the start back by one.*/ - req_waiting_head = req_waiting_head->prev; - - if (next == req_started_at) - break; - req = next; - } - req_head[i] = NULL; - } - } - + + while (req) { + struct request *next = req->next; + req->tx_count = req->reissue_count = 0; + req->ns = NULL; + /* ???? What to do about searches? */ + (void) evtimer_del(&req->timeout_event); + req->trans_id = 0; + req->transmit_me = 0; + + global_requests_waiting++; + evdns_request_insert(req, &req_waiting_head); + /* We want to insert these suspended elements at the front of + * the waiting queue, since they were pending before any of + * the waiting entries were added. This is a circular list, + * so we can just shift the start back by one.*/ + req_waiting_head = req_waiting_head->prev; + + if (next == req_started_at) + break; + req = next; + } + req_head = NULL; global_requests_inflight = 0; return 0; @@ -2277,9 +2245,7 @@ request_submit(struct request *const req if (req->ns) { /* if it has a nameserver assigned then this is going */ /* straight into the inflight queue */ - if (req_head) { - evdns_request_insert(req, &req_head[req->trans_id % global_max_nests]); - } + evdns_request_insert(req, &req_head); global_requests_inflight++; evdns_request_transmit(req); } else { @@ -2681,9 +2647,6 @@ evdns_set_option(const char *option, con log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", maxinflight); global_max_requests_inflight = maxinflight; - global_max_nests = global_max_requests_inflight / 10; - req_head = realloc (req_head, global_max_nests * sizeof (req_head[0])); - if (req_head == NULL) return -1; } else if (!strncmp(option, "attempts:", 9)) { int retries = strtoint(val); if (retries == -1) return -1; @@ -2751,7 +2714,7 @@ evdns_resolv_conf_parse(int flags, const u8 *resolv; char *start; int err = 0; - + log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); fd = open(filename, O_RDONLY); @@ -3003,28 +2966,9 @@ evdns_config_windows_nameservers(void) #endif int -evdns_requests_list_init(void) -{ - if (req_head == NULL) { - req_head = malloc (global_max_nests * sizeof (req_head[0])); - if (req_head == NULL) { - return 4; - } - memset (req_head, 0, global_max_nests * sizeof (req_head[0])); - } - - return 0; -} - -int evdns_init(void) { int res = 0; - - res = evdns_requests_list_init(); - if (res != 0) { - return res; - } #ifdef WIN32 res = evdns_config_windows_nameservers(); #else @@ -3058,14 +3002,11 @@ evdns_shutdown(int fail_requests) { struct nameserver *server, *server_next; struct search_domain *dom, *dom_next; - int i; - - for (i = 0; i < global_max_nests; i ++) { - while (req_head && req_head[i]) { - if (fail_requests) - reply_callback(req_head[i], 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_head[i], &req_head[i]); - } + + while (req_head) { + if (fail_requests) + reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(req_head, &req_head); } while (req_waiting_head) { if (fail_requests) diff -r 2609c564f854 -r d3e5ec3459f9 http.c --- a/http.c Mon Feb 18 18:55:03 2008 +0300 +++ b/http.c Wed Feb 13 15:31:39 2008 +0300 @@ -78,9 +78,6 @@ #ifdef HAVE_FCNTL_H #include #endif -#ifndef WIN32 -#include -#endif #undef timeout_pending #undef timeout_initialized @@ -2352,25 +2349,18 @@ name_from_addr(struct sockaddr *sa, sock static char ntop[NI_MAXHOST]; static char strport[NI_MAXSERV]; int ni_result; - - if (sa->sa_family == PF_UNIX) { - strncpy (ntop, ((struct sockaddr_un *)sa)->sun_path, NI_MAXHOST - 1); - strport[0] = '0'; - strport[1] = '\0'; - } - else { - if ((ni_result = getnameinfo(sa, salen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { - if (ni_result == EAI_SYSTEM) - event_err(1, "getnameinfo failed"); - else - event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); - } - } + + if ((ni_result = getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + if (ni_result == EAI_SYSTEM) + event_err(1, "getnameinfo failed"); + else + event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); + } + *phost = ntop; *pport = strport; - #else /* XXXX */ #endif diff -r 2609c564f854 -r d3e5ec3459f9 test/regress_dns.c --- a/test/regress_dns.c Mon Feb 18 18:55:03 2008 +0300 +++ b/test/regress_dns.c Wed Feb 13 15:31:39 2008 +0300 @@ -67,7 +67,6 @@ static int dns_err = 0; static int dns_err = 0; void dns_suite(void); -int evdns_requests_list_init(void); static void dns_gethostbyname_cb(int result, char type, int count, int ttl, @@ -304,11 +303,6 @@ dns_server(void) dns_ok = 1; fprintf(stdout, "DNS server support: "); - if (evdns_requests_list_init() != 0) { - fprintf(stdout, "Couldn't init dns requests hash list.\n"); - exit(1); - } - /* Add ourself as the only nameserver, and make sure we really are * the only nameserver. */ evdns_nameserver_ip_add("127.0.0.1:35353"); @@ -372,8 +366,8 @@ dns_suite(void) dns_suite(void) { dns_server(); /* Do this before we call evdns_init. */ + evdns_init(); - dns_gethostbyname(); dns_gethostbyname6(); dns_gethostbyaddr(); From vsevolod at highsecure.ru Mon Feb 18 10:58:32 2008 From: vsevolod at highsecure.ru (Vsevolod Stakhov) Date: Mon Feb 18 10:58:14 2008 Subject: [Libevent-users] [patch] replace linear search in evdns with hashed list and add support of listening on unix sockets for evhttp In-Reply-To: <47B9AAA0.8040706@highsecure.ru> References: <47B5BC4A.3010905@highsecure.ru> <20080216172630.GC21803@moria.seul.org> <47B980E1.60006@highsecure.ru> <47B9AAA0.8040706@highsecure.ru> Message-ID: <47B9AB28.6000909@highsecure.ru> Vsevolod Stakhov wrote: > Vsevolod Stakhov wrote: >> nickm@freehaven.net wrote: >>> On Fri, Feb 15, 2008 at 07:22:34PM +0300, Vsevolod Stakhov wrote: >>>> Hi, >>>> >>>> We found a problem with list requests_inflight while using evdns on >>>> highly loaded system (about 2000 DNS requests per second). First of >>>> all linear search is slow and second is that this list is not big >>>> enough to handle all requests, so many requests were in >>>> requests_waiting list, that has no limits on length. Due to this >>>> application grows in size constantly and is not able to serve >>>> requests. I've replaced linear list with hashed list, that improves >>>> performance. >>> >>> Hi! This looks pretty good. I'd like to apply it for trunk to apply >>> it to the next release after 1.4. (I want to avoid applying anything >>> but straight bugfixes to 1.4 at this point, so that it can get out of >>> beta soon.) A few comments and questions: > >> ... > > I've made some modifications to patch to attract your wishes, thought > I've not tested it in production yet. Sorry, attached reversed diff. -------------- next part -------------- diff -r d3e5ec3459f9 -r 2609c564f854 evdns.c --- a/evdns.c Wed Feb 13 15:31:39 2008 +0300 +++ b/evdns.c Mon Feb 18 18:55:03 2008 +0300 @@ -231,7 +231,7 @@ struct nameserver { char write_waiting; /* true if we are waiting for EV_WRITE events */ }; -static struct request *req_head = NULL, *req_waiting_head = NULL; +static struct request **req_head = NULL, *req_waiting_head = NULL; static struct nameserver *server_head = NULL; /* Represents a local port where we're listening for DNS requests. Right now, */ @@ -310,7 +310,9 @@ static int global_requests_inflight = 0; /* and are counted here */ static int global_requests_waiting = 0; -static int global_max_requests_inflight = 64; +static int global_max_requests_inflight = 1024; +/* numbers of nests in hashed list of pending requests */ +static int global_max_nests = 128; static struct timeval global_timeout = {5, 0}; /* 5 seconds */ static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */ @@ -443,13 +445,16 @@ _evdns_log(int warn, const char *fmt, .. /* failure */ static struct request * request_find_from_trans_id(u16 trans_id) { - struct request *req = req_head, *const started_at = req_head; - - if (req) { - do { - if (req->trans_id == trans_id) return req; - req = req->next; - } while (req != started_at); + struct request *req, *started_at; + if (req_head) { + req = req_head[trans_id % global_max_nests]; + started_at = req_head[trans_id % global_max_nests]; + if (req) { + do { + if (req->trans_id == trans_id) return req; + req = req->next; + } while (req != started_at); + } } return NULL; @@ -498,6 +503,7 @@ static void static void nameserver_failed(struct nameserver *const ns, const char *msg) { struct request *req, *started_at; + int i; /* if this nameserver has already been marked as failed */ /* then don't do anything */ if (!ns->state) return; @@ -527,19 +533,21 @@ nameserver_failed(struct nameserver *con /* if we don't have *any* good nameservers then there's no point */ /* trying to reassign requests to one */ - if (!global_good_nameservers) return; - - req = req_head; - started_at = req_head; - if (req) { - do { - if (req->tx_count == 0 && req->ns == ns) { - /* still waiting to go out, can be moved */ - /* to another server */ - req->ns = nameserver_pick(); - } - req = req->next; - } while (req != started_at); + if (!global_good_nameservers || !req_head) return; + + for (i = 0; i < global_max_nests; i ++) { + req = req_head[i]; + started_at = req_head[i]; + if (req) { + do { + if (req->tx_count == 0 && req->ns == ns) { + /* still waiting to go out, can be moved */ + /* to another server */ + req->ns = nameserver_pick(); + } + req = req->next; + } while (req != started_at); + } } } @@ -649,8 +657,10 @@ evdns_requests_pump_waiting_queue(void) req->ns = nameserver_pick(); request_trans_id_set(req, transaction_id_pick()); - - evdns_request_insert(req, &req_head); + + if (req_head) { + evdns_request_insert(req, &req_head[req->trans_id % global_max_nests]); + } evdns_request_transmit(req); evdns_transmit(); } @@ -742,19 +752,25 @@ reply_handle(struct request *const req, /* a new request was issued so this request is finished and */ /* the user callback will be made when that request (or a */ /* child of it) finishes. */ - request_finished(req, &req_head); + if (req_head) { + request_finished(req, &req_head[req->trans_id % global_max_nests]); + } return; } } /* all else failed. Pass the failure up */ reply_callback(req, 0, error, NULL); - request_finished(req, &req_head); + if (req_head) { + request_finished(req, &req_head[req->trans_id % global_max_nests]); + } } else { /* all ok, tell the user */ reply_callback(req, ttl, 0, reply); nameserver_up(req->ns); - request_finished(req, &req_head); + if (req_head) { + request_finished(req, &req_head[req->trans_id % global_max_nests]); + } } } @@ -1090,12 +1106,14 @@ static u16 static u16 transaction_id_pick(void) { for (;;) { - const struct request *req = req_head, *started_at; + const struct request *req = NULL, *started_at = NULL; u16 trans_id = trans_id_function(); if (trans_id == 0xffff) continue; /* now check to see if that id is already inflight */ - req = started_at = req_head; + if (req_head) { + req = started_at = req_head[trans_id % global_max_nests]; + } if (req) { do { if (req->trans_id == trans_id) break; @@ -1869,7 +1887,9 @@ evdns_request_timeout_callback(int fd, s if (req->tx_count >= global_max_retransmits) { /* this request has failed */ reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL); - request_finished(req, &req_head); + if (req_head) { + request_finished(req, &req_head[req->trans_id % global_max_nests]); + } } else { /* retransmit it */ evdns_request_transmit(req); @@ -1981,19 +2001,22 @@ nameserver_send_probe(struct nameserver /* 1 tried to transmit something */ static int evdns_transmit(void) { + int i; char did_try_to_transmit = 0; - - if (req_head) { - struct request *const started_at = req_head, *req = req_head; - /* first transmit all the requests which are currently waiting */ - do { - if (req->transmit_me) { - did_try_to_transmit = 1; - evdns_request_transmit(req); - } - - req = req->next; - } while (req != started_at); + + for (i = 0; i < global_max_nests; i ++) { + if (req_head && req_head[i]) { + struct request *const started_at = req_head[i], *req = req_head[i]; + /* first transmit all the requests which are currently waiting */ + do { + if (req->transmit_me) { + did_try_to_transmit = 1; + evdns_request_transmit(req); + } + + req = req->next; + } while (req != started_at); + } } return did_try_to_transmit; @@ -2019,7 +2042,8 @@ evdns_clear_nameservers_and_suspend(void evdns_clear_nameservers_and_suspend(void) { struct nameserver *server = server_head, *started_at = server_head; - struct request *req = req_head, *req_started_at = req_head; + struct request *req, *req_started_at; + int i; if (!server) return 0; @@ -2036,29 +2060,37 @@ evdns_clear_nameservers_and_suspend(void } server_head = NULL; global_good_nameservers = 0; - - while (req) { - struct request *next = req->next; - req->tx_count = req->reissue_count = 0; - req->ns = NULL; - /* ???? What to do about searches? */ - (void) evtimer_del(&req->timeout_event); - req->trans_id = 0; - req->transmit_me = 0; - - global_requests_waiting++; - evdns_request_insert(req, &req_waiting_head); - /* We want to insert these suspended elements at the front of - * the waiting queue, since they were pending before any of - * the waiting entries were added. This is a circular list, - * so we can just shift the start back by one.*/ - req_waiting_head = req_waiting_head->prev; - - if (next == req_started_at) - break; - req = next; - } - req_head = NULL; + + if (req_head) { + for (i = 0; i < global_max_nests; i ++) { + req = req_head[i]; + req_started_at = req; + + while (req) { + struct request *next = req->next; + req->tx_count = req->reissue_count = 0; + req->ns = NULL; + /* ???? What to do about searches? */ + (void) evtimer_del(&req->timeout_event); + req->trans_id = 0; + req->transmit_me = 0; + + global_requests_waiting++; + evdns_request_insert(req, &req_waiting_head); + /* We want to insert these suspended elements at the front of + * the waiting queue, since they were pending before any of + * the waiting entries were added. This is a circular list, + * so we can just shift the start back by one.*/ + req_waiting_head = req_waiting_head->prev; + + if (next == req_started_at) + break; + req = next; + } + req_head[i] = NULL; + } + } + global_requests_inflight = 0; return 0; @@ -2245,7 +2277,9 @@ request_submit(struct request *const req if (req->ns) { /* if it has a nameserver assigned then this is going */ /* straight into the inflight queue */ - evdns_request_insert(req, &req_head); + if (req_head) { + evdns_request_insert(req, &req_head[req->trans_id % global_max_nests]); + } global_requests_inflight++; evdns_request_transmit(req); } else { @@ -2647,6 +2681,9 @@ evdns_set_option(const char *option, con log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d", maxinflight); global_max_requests_inflight = maxinflight; + global_max_nests = global_max_requests_inflight / 10; + req_head = realloc (req_head, global_max_nests * sizeof (req_head[0])); + if (req_head == NULL) return -1; } else if (!strncmp(option, "attempts:", 9)) { int retries = strtoint(val); if (retries == -1) return -1; @@ -2714,7 +2751,7 @@ evdns_resolv_conf_parse(int flags, const u8 *resolv; char *start; int err = 0; - + log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename); fd = open(filename, O_RDONLY); @@ -2966,9 +3003,28 @@ evdns_config_windows_nameservers(void) #endif int +evdns_requests_list_init(void) +{ + if (req_head == NULL) { + req_head = malloc (global_max_nests * sizeof (req_head[0])); + if (req_head == NULL) { + return 4; + } + memset (req_head, 0, global_max_nests * sizeof (req_head[0])); + } + + return 0; +} + +int evdns_init(void) { int res = 0; + + res = evdns_requests_list_init(); + if (res != 0) { + return res; + } #ifdef WIN32 res = evdns_config_windows_nameservers(); #else @@ -3002,11 +3058,14 @@ evdns_shutdown(int fail_requests) { struct nameserver *server, *server_next; struct search_domain *dom, *dom_next; - - while (req_head) { - if (fail_requests) - reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL); - request_finished(req_head, &req_head); + int i; + + for (i = 0; i < global_max_nests; i ++) { + while (req_head && req_head[i]) { + if (fail_requests) + reply_callback(req_head[i], 0, DNS_ERR_SHUTDOWN, NULL); + request_finished(req_head[i], &req_head[i]); + } } while (req_waiting_head) { if (fail_requests) diff -r d3e5ec3459f9 -r 2609c564f854 http.c --- a/http.c Wed Feb 13 15:31:39 2008 +0300 +++ b/http.c Mon Feb 18 18:55:03 2008 +0300 @@ -78,6 +78,9 @@ #ifdef HAVE_FCNTL_H #include #endif +#ifndef WIN32 +#include +#endif #undef timeout_pending #undef timeout_initialized @@ -2349,18 +2352,25 @@ name_from_addr(struct sockaddr *sa, sock static char ntop[NI_MAXHOST]; static char strport[NI_MAXSERV]; int ni_result; - - if ((ni_result = getnameinfo(sa, salen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { - if (ni_result == EAI_SYSTEM) - event_err(1, "getnameinfo failed"); - else - event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); - } - + + if (sa->sa_family == PF_UNIX) { + strncpy (ntop, ((struct sockaddr_un *)sa)->sun_path, NI_MAXHOST - 1); + strport[0] = '0'; + strport[1] = '\0'; + } + else { + if ((ni_result = getnameinfo(sa, salen, + ntop, sizeof(ntop), strport, sizeof(strport), + NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { + if (ni_result == EAI_SYSTEM) + event_err(1, "getnameinfo failed"); + else + event_errx(1, "getnameinfo failed: %s", gai_strerror(ni_result)); + } + } *phost = ntop; *pport = strport; + #else /* XXXX */ #endif diff -r d3e5ec3459f9 -r 2609c564f854 test/regress_dns.c --- a/test/regress_dns.c Wed Feb 13 15:31:39 2008 +0300 +++ b/test/regress_dns.c Mon Feb 18 18:55:03 2008 +0300 @@ -67,6 +67,7 @@ static int dns_err = 0; static int dns_err = 0; void dns_suite(void); +int evdns_requests_list_init(void); static void dns_gethostbyname_cb(int result, char type, int count, int ttl, @@ -303,6 +304,11 @@ dns_server(void) dns_ok = 1; fprintf(stdout, "DNS server support: "); + if (evdns_requests_list_init() != 0) { + fprintf(stdout, "Couldn't init dns requests hash list.\n"); + exit(1); + } + /* Add ourself as the only nameserver, and make sure we really are * the only nameserver. */ evdns_nameserver_ip_add("127.0.0.1:35353"); @@ -366,8 +372,8 @@ dns_suite(void) dns_suite(void) { dns_server(); /* Do this before we call evdns_init. */ - evdns_init(); + dns_gethostbyname(); dns_gethostbyname6(); dns_gethostbyaddr(); From hmd at mail.ru Tue Feb 19 11:19:44 2008 From: hmd at mail.ru (Eugene 'HMage' Bujak) Date: Tue Feb 19 11:20:07 2008 Subject: [Libevent-users] strange libevent performance Message-ID: <47BB01A0.2030901@mail.ru> Hi. I've been doing some pipe performance tests (for heavy inter-thread communication, so I can have callbacks called only when there's data to be handled) and I've figured out some strange libevent overhead. The event_base_loop() take 22% of self time and event_queue_remove() plus event_queue_insert() take 14% of self time each. Am I doing something wrong? Libevent version - 1.4.1beta. Gprof output and source code are in attachment. Be sure to compile libevent with -pg flag and statically link too to have libevent's functions included into timing. Let me know if it's not reproducible, I'll appreciate any help. -- - Eugene 'HMage' Bujak. -------------- next part -------------- #include #include #include #include #include #include #include #include #include #include // required for event.h #include // C++ #include using namespace std; pthread_mutex_t stdout_mutex = PTHREAD_MUTEX_INITIALIZER; int filedes1[2]; int filedes2[2]; int thread1readcount = 0; int thread2readcount = 0; int lastread = 0; struct timeval tv_bench; struct event ev_bench; #define BENCH_INTERVAL 1000 // in ms #define IN #define OUT #ifdef _WIN32 typedef uint32_t socket_t; #endif #ifdef unix typedef int socket_t; #endif void ms_to_timeval (uint32_t IN ms, struct timeval OUT * tv) { tv->tv_sec = (ms) / 1000; //(long)floor(ms * 0.001); tv->tv_usec = (ms - (tv->tv_sec * 1000)) * 1000; } int fd_nonblock (socket_t socket) { #ifndef _WIN32 int yes = 1; return ioctl (socket, FIONBIO, &yes); #else unsigned long yes = 1; return ioctlsocket (socket, FIONBIO, (unsigned long *) &yes); #endif } inline int net_errno (void) { #ifdef _WIN32 return WSAGetLastError (); #else return errno; #endif } void sig_term (int i) { static int si = 0; i = i; // Save the settings before returning back to default signal handler signal (SIGINT, SIG_DFL); signal (SIGTERM, SIG_DFL); #ifndef _WIN32 // Windows doesn't define this signal signal (SIGHUP, SIG_DFL); signal (SIGPIPE, SIG_DFL); #endif if (!si) { si++; // cleanup (); #ifdef _WIN32 _exit (0); #else exit (0); #endif } } void on_bench (int fd, short ev, void *arg) { fprintf (stdout, "read_per_sec = %i\n", thread1readcount - lastread); lastread = thread1readcount; ms_to_timeval (BENCH_INTERVAL, &tv_bench); event_add (&ev_bench, &tv_bench); } typedef struct thread_s { thread_s () { base = NULL; buf_read = evbuffer_new (); buf_write = evbuffer_new (); readcount = NULL; } event_base *base; struct event ev_read; struct event ev_write; int fd_read; int fd_write; struct evbuffer *buf_read; struct evbuffer *buf_write; int *readcount; } thread_t; void on_thread_read (int fd_read, short ev, void *arg) { thread_t *thread = (thread_t *) arg; // fprintf(stdout, "on_thread_read(%p)\n", thread); int remtoread = 4 - EVBUFFER_LENGTH (thread->buf_read); if (remtoread) { int retval = evbuffer_read (thread->buf_read, fd_read, remtoread); if ((retval < 0) && (net_errno () == EINTR)) return; // call would block, try later if ((retval < 0) && (net_errno () == EWOULDBLOCK)) return; // call would block, try later if ((retval < 0) && (net_errno () == EAGAIN)) return; // call would block, try later if (retval < 0) { int error = net_errno (); perror ("read(pipe)"); event_del (&thread->ev_read); event_del (&thread->ev_write); return; } if (retval == 0) { return; } } if (EVBUFFER_LENGTH (thread->buf_read) != 4) { fprintf (stderr, "buf_read length != 4\n"); // event_del(&thread->ev_read); // event_del(&thread->ev_write); return; } int recvint = ((int *) EVBUFFER_DATA (thread->buf_read))[0]; if (recvint != *thread->readcount) { // fprintf(stderr, "received data is not valid = %i, expected %i\n", recvint, *thread->readcount); // event_del(&thread->ev_read); // event_del(&thread->ev_write); // return; } evbuffer_add (thread->buf_write, thread->readcount, 4); evbuffer_drain (thread->buf_read, 4); event_add (&thread->ev_write, NULL); (*thread->readcount)++; } void on_thread_write (int fd_write, short ev, void *arg) { thread_t *thread = (thread_t *) arg; // fprintf(stdout, "on_thread_write(%p)\n", thread); if (EVBUFFER_LENGTH (thread->buf_write) == 0) { event_del (&thread->ev_write); return; } int retval = evbuffer_write (thread->buf_write, fd_write); if ((retval < 0) && (net_errno () == EINTR)) return; // call would block, try later if ((retval < 0) && (net_errno () == EWOULDBLOCK)) return; // call would block, try later if ((retval < 0) && (net_errno () == EAGAIN)) return; // call would block, try later if (retval == 0) { // That's strange, send() function was successful but we've written nothing fprintf (stderr, "SHOULD NOT HAPPEN -- Had send() ready, but zero bytes were written\n"); return; } if (retval <= 0) { int error = net_errno (); perror ("write(pipe)"); event_del (&thread->ev_read); event_del (&thread->ev_write); return; } if (EVBUFFER_LENGTH (thread->buf_write) == 0) { // no more data to send event_del (&thread->ev_write); } } void thread_initevent (thread_t * thread) { int retval = 0; thread->base = (event_base *) event_init (); event_set (&thread->ev_read, thread->fd_read, EV_READ | EV_PERSIST, on_thread_read, thread); event_set (&thread->ev_write, thread->fd_write, EV_WRITE | EV_PERSIST, on_thread_write, thread); retval = event_base_set (thread->base, &thread->ev_read); if (retval < 0) perror ("event_base_set"); retval = event_base_set (thread->base, &thread->ev_write); if (retval < 0) perror ("event_base_set"); retval = event_add (&thread->ev_read, NULL); if (retval < 0) perror ("event_add"); } void * thread1_main (void *arg) { thread_t thread; thread.fd_read = filedes2[0]; thread.fd_write = filedes1[1]; thread.readcount = &thread1readcount; thread_initevent (&thread); event_base_dispatch (thread.base); return NULL; } void * thread2_main (void *arg) { thread_t thread; thread.fd_read = filedes1[0]; thread.fd_write = filedes2[1]; thread.readcount = &thread2readcount; thread_initevent (&thread); evbuffer_add (thread.buf_write, thread.readcount, 4); event_add (&thread.ev_write, NULL); event_base_dispatch (thread.base); return NULL; } int main (int argc, char *argv[]) { int retval = 0; pthread_t threads[2]; signal(SIGINT, sig_term); signal(SIGTERM, sig_term); #ifndef _WIN32 // Windows doesn't define these signals signal(SIGHUP, sig_term); signal(SIGPIPE, sig_term); #endif retval = pipe (filedes1); if (retval < 0) { perror ("pipe"); return 1; } retval = pipe (filedes2); if (retval < 0) { perror ("pipe"); return 1; } retval = fd_nonblock (filedes1[0]); if (retval < 0) { perror ("fd_nonblock"); return 1; } retval = fd_nonblock (filedes1[1]); if (retval < 0) { perror ("fd_nonblock"); return 1; } retval = fd_nonblock (filedes2[0]); if (retval < 0) { perror ("fd_nonblock"); return 1; } retval = fd_nonblock (filedes2[1]); if (retval < 0) { perror ("fd_nonblock"); return 1; } retval = pthread_create (&threads[0], NULL, thread1_main, NULL); if (retval) { fprintf (stderr, "Couldn't create thread.\n"); return 1; } retval = pthread_create (&threads[1], NULL, thread2_main, NULL); if (retval) { fprintf (stderr, "Couldn't create thread 2.\n"); pthread_cancel (threads[0]); return 1; } event_base *base = (event_base *) event_init (); event_set (&ev_bench, -1, 0, on_bench, NULL); event_base_set (base, &ev_bench); ms_to_timeval (BENCH_INTERVAL, &tv_bench); event_add (&ev_bench, &tv_bench); event_base_dispatch (base); } -------------- next part -------------- Flat profile: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 22.58 0.07 0.07 3 23.34 100.02 event_base_loop 14.52 0.12 0.05 1327431 0.00 0.00 event_queue_remove 14.52 0.16 0.05 1324910 0.00 0.00 event_queue_insert 9.68 0.19 0.03 836768 0.00 0.00 epoll_dispatch 9.68 0.22 0.03 835087 0.00 0.00 epoll_wait 6.45 0.24 0.02 362924 0.00 0.00 evbuffer_read 3.23 0.25 0.01 442825 0.00 0.00 on_thread_write(int, short, void*) 3.23 0.26 0.01 442774 0.00 0.00 event_add 3.23 0.27 0.01 442763 0.00 0.00 epoll_add 3.23 0.28 0.01 442606 0.00 0.00 on_thread_read(int, short, void*) 3.23 0.29 0.01 442505 0.00 0.00 event_del 1.61 0.30 0.01 882060 0.00 0.00 event_active 1.61 0.30 0.01 442824 0.00 0.00 evbuffer_write 1.61 0.31 0.01 evbuffer_expand 1.61 0.31 0.01 event_loopbreak 0.00 0.31 0.00 885234 0.00 0.00 epoll_ctl 0.00 0.31 0.00 442759 0.00 0.00 evbuffer_drain 0.00 0.31 0.00 442726 0.00 0.00 evbuffer_add 0.00 0.31 0.00 442542 0.00 0.00 epoll_del 0.00 0.31 0.00 12 0.00 0.00 evsignal_process 0.00 0.31 0.00 8 0.00 0.00 event_set 0.00 0.31 0.00 7 0.00 0.00 on_bench(int, short, void*) 0.00 0.31 0.00 5 0.00 0.00 event_base_set 0.00 0.31 0.00 3 0.00 0.00 epoll_create 0.00 0.31 0.00 3 0.00 0.00 epoll_init 0.00 0.31 0.00 3 0.00 0.00 evbuffer_new 0.00 0.31 0.00 3 0.00 100.02 event_base_dispatch 0.00 0.31 0.00 3 0.00 0.00 event_base_new 0.00 0.31 0.00 3 0.00 0.00 event_base_priority_init 0.00 0.31 0.00 3 0.00 0.00 event_init 0.00 0.31 0.00 3 0.00 0.00 evsignal_init 0.00 0.31 0.00 3 0.00 0.00 evutil_make_socket_nonblocking 0.00 0.31 0.00 3 0.00 0.00 evutil_socketpair 0.00 0.31 0.00 2 0.00 0.00 thread_initevent(thread_s*) % the percentage of the total running time of the time program used by this function. cumulative a running sum of the number of seconds accounted seconds for by this function and those listed above it. self the number of seconds accounted for by this seconds function alone. This is the major sort for this listing. calls the number of times this function was invoked, if this function is profiled, else blank. self the average number of milliseconds spent in this ms/call function per call, if this function is profiled, else blank. total the average number of milliseconds spent in this ms/call function and its descendents per call, if this function is profiled, else blank. name the name of the function. This is the minor sort for this listing. The index shows the location of the function in the gprof listing. If the index is in parenthesis it shows where it would appear in the gprof listing if it were to be printed. Call graph (explanation follows) granularity: each sample hit covers 2 byte(s) for 3.23% of 0.31 seconds index % time self children called name 0.00 0.10 1/3 thread2_main(void*) [3] 0.00 0.10 1/3 thread1_main(void*) [4] 0.00 0.10 1/3 main [5] [1] 96.8 0.00 0.30 3 event_base_dispatch [1] 0.07 0.23 3/3 event_base_loop [2] ----------------------------------------------- 0.07 0.23 3/3 event_base_dispatch [1] [2] 96.8 0.07 0.23 3 event_base_loop [2] 0.03 0.06 836768/836768 epoll_dispatch [6] 0.01 0.06 442606/442606 on_thread_read(int, short, void*) [7] 0.03 0.00 884926/1327431 event_queue_remove [8] 0.01 0.02 442505/442505 event_del [12] 0.01 0.01 442825/442825 on_thread_write(int, short, void*) [14] 0.00 0.00 7/442774 event_add [11] 0.00 0.00 7/1324910 event_queue_insert [9] 0.00 0.00 7/7 on_bench(int, short, void*) [119] ----------------------------------------------- [3] 32.3 0.00 0.10 thread2_main(void*) [3] 0.00 0.10 1/3 event_base_dispatch [1] 0.00 0.00 1/2 thread_initevent(thread_s*) [20] 0.00 0.00 1/442774 event_add [11] 0.00 0.00 1/442763 epoll_add [15] 0.00 0.00 1/3 evbuffer_new [30] ----------------------------------------------- [4] 32.3 0.00 0.10 thread1_main(void*) [4] 0.00 0.10 1/3 event_base_dispatch [1] 0.00 0.00 1/2 thread_initevent(thread_s*) [20] 0.00 0.00 2/3 evbuffer_new [30] ----------------------------------------------- [5] 32.3 0.00 0.10 main [5] 0.00 0.10 1/3 event_base_dispatch [1] 0.00 0.00 1/442774 event_add [11] 0.00 0.00 1/3 event_init [33] 0.00 0.00 1/8 event_set [26] 0.00 0.00 1/5 event_base_set [27] ----------------------------------------------- 0.03 0.06 836768/836768 event_base_loop [2] [6] 30.6 0.03 0.06 836768 epoll_dispatch [6] 0.03 0.00 835087/835087 epoll_wait [10] 0.03 0.00 882125/1324910 event_queue_insert [9] 0.01 0.00 882060/882060 event_active [16] 0.00 0.00 12/12 evsignal_process [25] ----------------------------------------------- 0.01 0.06 442606/442606 event_base_loop [2] [7] 21.0 0.01 0.06 442606 on_thread_read(int, short, void*) [7] 0.01 0.02 442763/442774 event_add [11] 0.02 0.00 362924/362924 evbuffer_read [13] 0.01 0.00 442760/442763 epoll_add [15] 0.00 0.00 442759/442759 evbuffer_drain [22] 0.00 0.00 442726/442726 evbuffer_add [23] ----------------------------------------------- 0.02 0.00 442505/1327431 event_del [12] 0.03 0.00 884926/1327431 event_base_loop [2] [8] 14.5 0.05 0.00 1327431 event_queue_remove [8] ----------------------------------------------- 0.00 0.00 7/1324910 event_base_loop [2] 0.02 0.00 442778/1324910 event_add [11] 0.03 0.00 882125/1324910 epoll_dispatch [6] [9] 14.5 0.05 0.00 1324910 event_queue_insert [9] ----------------------------------------------- 0.03 0.00 835087/835087 epoll_dispatch [6] [10] 9.7 0.03 0.00 835087 epoll_wait [10] ----------------------------------------------- 0.00 0.00 1/442774 thread2_main(void*) [3] 0.00 0.00 1/442774 main [5] 0.00 0.00 2/442774 thread_initevent(thread_s*) [20] 0.00 0.00 7/442774 event_base_loop [2] 0.01 0.02 442763/442774 on_thread_read(int, short, void*) [7] [11] 8.1 0.01 0.02 442774 event_add [11] 0.02 0.00 442778/1324910 event_queue_insert [9] ----------------------------------------------- 0.01 0.02 442505/442505 event_base_loop [2] [12] 8.1 0.01 0.02 442505 event_del [12] 0.02 0.00 442505/1327431 event_queue_remove [8] 0.00 0.00 442542/442542 epoll_del [24] ----------------------------------------------- 0.02 0.00 362924/362924 on_thread_read(int, short, void*) [7] [13] 6.5 0.02 0.00 362924 evbuffer_read [13] ----------------------------------------------- 0.01 0.01 442825/442825 event_base_loop [2] [14] 4.8 0.01 0.01 442825 on_thread_write(int, short, void*) [14] 0.01 0.00 442824/442824 evbuffer_write [17] ----------------------------------------------- 0.00 0.00 1/442763 thread2_main(void*) [3] 0.00 0.00 2/442763 thread_initevent(thread_s*) [20] 0.01 0.00 442760/442763 on_thread_read(int, short, void*) [7] [15] 3.2 0.01 0.00 442763 epoll_add [15] 0.00 0.00 442734/885234 epoll_ctl [21] ----------------------------------------------- 0.01 0.00 882060/882060 epoll_dispatch [6] [16] 1.6 0.01 0.00 882060 event_active [16] ----------------------------------------------- 0.01 0.00 442824/442824 on_thread_write(int, short, void*) [14] [17] 1.6 0.01 0.00 442824 evbuffer_write [17] ----------------------------------------------- [18] 1.6 0.01 0.00 evbuffer_expand [18] ----------------------------------------------- [19] 1.6 0.01 0.00 event_loopbreak [19] ----------------------------------------------- 0.00 0.00 1/2 thread2_main(void*) [3] 0.00 0.00 1/2 thread1_main(void*) [4] [20] 0.0 0.00 0.00 2 thread_initevent(thread_s*) [20] 0.00 0.00 2/442774 event_add [11] 0.00 0.00 2/442763 epoll_add [15] 0.00 0.00 4/8 event_set [26] 0.00 0.00 4/5 event_base_set [27] 0.00 0.00 2/3 event_init [33] ----------------------------------------------- 0.00 0.00 442500/885234 epoll_del [24] 0.00 0.00 442734/885234 epoll_add [15] [21] 0.0 0.00 0.00 885234 epoll_ctl [21] ----------------------------------------------- 0.00 0.00 442759/442759 on_thread_read(int, short, void*) [7] [22] 0.0 0.00 0.00 442759 evbuffer_drain [22] ----------------------------------------------- 0.00 0.00 442726/442726 on_thread_read(int, short, void*) [7] [23] 0.0 0.00 0.00 442726 evbuffer_add [23] ----------------------------------------------- 0.00 0.00 442542/442542 event_del [12] [24] 0.0 0.00 0.00 442542 epoll_del [24] 0.00 0.00 442500/885234 epoll_ctl [21] ----------------------------------------------- 0.00 0.00 12/12 epoll_dispatch [6] [25] 0.0 0.00 0.00 12 evsignal_process [25] ----------------------------------------------- 0.00 0.00 1/8 main [5] 0.00 0.00 3/8 evsignal_init [34] 0.00 0.00 4/8 thread_initevent(thread_s*) [20] [26] 0.0 0.00 0.00 8 event_set [26] ----------------------------------------------- 0.00 0.00 1/5 main [5] 0.00 0.00 4/5 thread_initevent(thread_s*) [20] [27] 0.0 0.00 0.00 5 event_base_set [27] ----------------------------------------------- 0.00 0.00 3/3 epoll_init [29] [28] 0.0 0.00 0.00 3 epoll_create [28] ----------------------------------------------- 0.00 0.00 3/3 event_base_new [31] [29] 0.0 0.00 0.00 3 epoll_init [29] 0.00 0.00 3/3 epoll_create [28] 0.00 0.00 3/3 evsignal_init [34] ----------------------------------------------- 0.00 0.00 1/3 thread2_main(void*) [3] 0.00 0.00 2/3 thread1_main(void*) [4] [30] 0.0 0.00 0.00 3 evbuffer_new [30] ----------------------------------------------- 0.00 0.00 3/3 event_init [33] [31] 0.0 0.00 0.00 3 event_base_new [31] 0.00 0.00 3/3 epoll_init [29] 0.00 0.00 3/3 event_base_priority_init [32] ----------------------------------------------- 0.00 0.00 3/3 event_base_new [31] [32] 0.0 0.00 0.00 3 event_base_priority_init [32] ----------------------------------------------- 0.00 0.00 1/3 main [5] 0.00 0.00 2/3 thread_initevent(thread_s*) [20] [33] 0.0 0.00 0.00 3 event_init [33] 0.00 0.00 3/3 event_base_new [31] ----------------------------------------------- 0.00 0.00 3/3 epoll_init [29] [34] 0.0 0.00 0.00 3 evsignal_init [34] 0.00 0.00 3/3 evutil_socketpair [36] 0.00 0.00 3/3 evutil_make_socket_nonblocking [35] 0.00 0.00 3/8 event_set [26] ----------------------------------------------- 0.00 0.00 3/3 evsignal_init [34] [35] 0.0 0.00 0.00 3 evutil_make_socket_nonblocking [35] ----------------------------------------------- 0.00 0.00 3/3 evsignal_init [34] [36] 0.0 0.00 0.00 3 evutil_socketpair [36] ----------------------------------------------- 0.00 0.00 7/7 event_base_loop [2] [119] 0.0 0.00 0.00 7 on_bench(int, short, void*) [119] ----------------------------------------------- This table describes the call tree of the program, and was sorted by the total amount of time spent in each function and its children. Each entry in this table consists of several lines. The line with the index number at the left hand margin lists the current function. The lines above it list the functions that called this function, and the lines below it list the functions this one called. This line lists: index A unique number given to each element of the table. Index numbers are sorted numerically. The index number is printed next to every function name so it is easier to look up where the function in the table. % time This is the percentage of the `total' time that was spent in this function and its children. Note that due to different viewpoints, functions excluded by options, etc, these numbers will NOT add up to 100%. self This is the total amount of time spent in this function. children This is the total amount of time propagated into this function by its children. called This is the number of times the function was called. If the function called itself recursively, the number only includes non-recursive calls, and is followed by a `+' and the number of recursive calls. name The name of the current function. The index number is printed after it. If the function is a member of a cycle, the cycle number is printed between the function's name and the index number. For the function's parents, the fields have the following meanings: self This is the amount of time that was propagated directly from the function into this parent. children This is the amount of time that was propagated from the function's children into this parent. called This is the number of times this parent called the function `/' the total number of times the function was called. Recursive calls to the function are not included in the number after the `/'. name This is the name of the parent. The parent's index number is printed after it. If the parent is a member of a cycle, the cycle number is printed between the name and the index number. If the parents of the function cannot be determined, the word `' is printed in the `name' field, and all the other fields are blank. For the function's children, the fields have the following meanings: self This is the amount of time that was propagated directly from the child into the function. children This is the amount of time that was propagated from the child's children to the function. called This is the number of times the function called this child `/' the total number of times the child was called. Recursive calls by the child are not listed in the number after the `/'. name This is the name of the child. The child's index number is printed after it. If the child is a member of a cycle, the cycle number is printed between the name and the index number. If there are any cycles (circles) in the call graph, there is an entry for the cycle-as-a-whole. This entry shows who called the cycle (as parents) and the members of the cycle (as children.) The `+' recursive calls entry shows the number of function calls that were internal to the cycle, and the calls entry for each member shows, for that member, how many times it was called from other members of the cycle. Index by function name [7] on_thread_read(int, short, void*) [22] evbuffer_drain [12] event_del [14] on_thread_write(int, short, void*) [18] evbuffer_expand [33] event_init [20] thread_initevent(thread_s*) [30] evbuffer_new [19] event_loopbreak [119] on_bench(int, short, void*) [13] evbuffer_read [9] event_queue_insert (event.c) [15] epoll_add [17] evbuffer_write [8] event_queue_remove (event.c) [28] epoll_create [16] event_active [26] event_set [21] epoll_ctl [11] event_add [34] evsignal_init [24] epoll_del [1] event_base_dispatch [25] evsignal_process [6] epoll_dispatch [2] event_base_loop [35] evutil_make_socket_nonblocking [29] epoll_init [31] event_base_new [36] evutil_socketpair [10] epoll_wait [32] event_base_priority_init [23] evbuffer_add [27] event_base_set From tani at tanihosokawa.org Wed Feb 20 16:16:16 2008 From: tani at tanihosokawa.org (Tani Hosokawa) Date: Wed Feb 20 16:18:25 2008 Subject: [Libevent-users] minor documentation bug Message-ID: <47BC98A0.6040600@tanihosokawa.org> In the documentation for event_base_loop() the return values should actually be: 0 if successful, -1 if an error occurred, or 1 if no events were registered From provos at citi.umich.edu Wed Feb 20 16:53:53 2008 From: provos at citi.umich.edu (Niels Provos) Date: Wed Feb 20 16:54:14 2008 Subject: [Libevent-users] minor documentation bug In-Reply-To: <47BC98A0.6040600@tanihosokawa.org> References: <47BC98A0.6040600@tanihosokawa.org> Message-ID: <850f7cbe0802201353o6b19f3f1q660ff3ca29ba26b0@mail.gmail.com> Thank you. This has been fixed in trunk and patches-1.4 Niels. On Wed, Feb 20, 2008 at 1:16 PM, Tani Hosokawa wrote: > In the documentation for event_base_loop() the return values should > actually be: > > 0 if successful, -1 if an error occurred, or 1 if no events were registered > _______________________________________________ > Libevent-users mailing list > Libevent-users@monkey.org > http://monkeymail.org/mailman/listinfo/libevent-users > > From 6622n8e02 at sneakemail.com Fri Feb 22 04:54:57 2008 From: 6622n8e02 at sneakemail.com (Frank Schaeckermann) Date: Fri Feb 22 05:02:58 2008 Subject: [Libevent-users] libevent (svn revision 651) segfaults on linux 2.6.12, ARM little endian architecture Message-ID: <17358-65347@sneakemail.com> I have installed transmission-daemon (a Bittorrent client) svn revision 5051 on my Maxtor Shared Storage II (ARM little endian) with Linux 2.6.12. Very soon after starting the daemon it segfaults. I have re-compiled and re-installed transmission-daemon with debug information and ran it in gdb. The segfault happens in epoll.c line 217 (libevent svn revision 651) in epoll_dispatch. Apparently an event comes in that does not have its data pointer set properly. I fixed the problem by adding a check for a valid data pointer before doing anything with it and now transmission does not segfault anymore. But the event that is to be dispatched now does not do anything anymore. Which means I have cured the symptoms but not the cause. Somebody needs to figure out, why and from where invalid events get to epoll_dispatch. Or maybe it is okay if that happens, but then epoll.c needs to be patched accordingly. If there is anything I can help with, please let me know! Regards, Frank P.S. I have already posted this in the transmissionbt forum but was asked by the developers to report it upstream to libevent as well. From 6622n8e02 at sneakemail.com Fri Feb 22 13:14:58 2008 From: 6622n8e02 at sneakemail.com (Frank Schaeckermann) Date: Fri Feb 22 13:16:18 2008 Subject: [Libevent-users] libevent (svn revision 651) segfaults on linux 2.6.12, ARM little endian architecture In-Reply-To: <850f7cbe0802220800n4433680fi77f269ddb16bbc8e@mail.gmail.com> References: <17358-65347@sneakemail.com> <850f7cbe0802220800n4433680fi77f269ddb16bbc8e@mail.gmail.com> Message-ID: <19526-66682@sneakemail.com> Yep... the build process actually pulled a completely fresh copy of libevent when compiling the transmission-daemon. I'll do some more debugging over the weekend. Maybe I can find out, where the rogue event is coming from. Frank Niels Provos provos-at-citi.umich.edu |libevent| wrote: > Here is a sil