]> granicus.if.org Git - libevent/commitdiff
evdns: New flag to make evdns not prevent the event loop from exiting
authorAzat Khuzhin <a3at.mail@gmail.com>
Wed, 27 Mar 2013 16:15:46 +0000 (20:15 +0400)
committerNick Mathewson <nickm@torproject.org>
Thu, 25 Apr 2013 14:43:12 +0000 (10:43 -0400)
Here is the brief description of problem:
When you are use evdns to resolve domains to IP adresses (see
./sample/dns-example) you loop never returns from event_base_dispatch(),
and because of this the program will never terminated.

Because existing programs may be depending on the old behavior, we
only apply the fix when evdns_base_new() is created with a new flag -
EVDNS_BASE_DISABLE_WHEN_INACTIVE.

 (Commit message edited by Nick while squashing the branch.)

evdns.c
include/event2/dns.h
sample/dns-example.c

diff --git a/evdns.c b/evdns.c
index f42aa6098ba6ea3d0fdeea01638da2f1536fe484..f805aedd7a902adc2df30b96f11a318ff89f5a81 100644 (file)
--- a/evdns.c
+++ b/evdns.c
@@ -236,6 +236,10 @@ struct nameserver {
        char choked;  /* true if we have an EAGAIN from this server's socket */
        char write_waiting;  /* true if we are waiting for EV_WRITE events */
        struct evdns_base *base;
+
+       /* Number of currently inflight requests: used
+        * to track when we should add/del the event. */
+       int requests_inflight;
 };
 
 
@@ -358,6 +362,8 @@ struct evdns_base {
 #ifndef EVENT__DISABLE_THREAD_SUPPORT
        void *lock;
 #endif
+
+       int disable_when_inactive;
 };
 
 struct hosts_entry {
@@ -660,12 +666,19 @@ request_finished(struct request *const req, struct request **head, int free_hand
        if (was_inflight) {
                evtimer_del(&req->timeout_event);
                base->global_requests_inflight--;
+               req->ns->requests_inflight--;
        } else {
                base->global_requests_waiting--;
        }
        /* it was initialized during request_new / evtimer_assign */
        event_debug_unassign(&req->timeout_event);
 
+       if (req->ns &&
+           req->ns->requests_inflight == 0 &&
+           req->base->disable_when_inactive) {
+               event_del(&req->ns->event);
+       }
+
        if (!req->request_appended) {
                /* need to free the request data on it's own */
                mm_free(req->request);
@@ -744,6 +757,8 @@ evdns_requests_pump_waiting_queue(struct evdns_base *base) {
                base->global_requests_inflight++;
 
                req->ns = nameserver_pick(base);
+               req->ns->requests_inflight++;
+
                request_trans_id_set(req, transaction_id_pick(base));
 
                evdns_request_insert(req, &REQ_HEAD(base, req->trans_id));
@@ -2188,6 +2203,13 @@ evdns_request_transmit_to(struct request *req, struct nameserver *server) {
        int r;
        ASSERT_LOCKED(req->base);
        ASSERT_VALID_REQUEST(req);
+
+       if (server->requests_inflight == 1 &&
+               req->base->disable_when_inactive &&
+               event_add(&server->event, NULL) < 0) {
+               return 1;
+       }
+
        r = sendto(server->socket, (void*)req->request, req->request_len, 0,
            (struct sockaddr *)&server->address, server->addrlen);
        if (r < 0) {
@@ -2496,8 +2518,9 @@ evdns_nameserver_add_impl_(struct evdns_base *base, const struct sockaddr *addre
        memcpy(&ns->address, address, addrlen);
        ns->addrlen = addrlen;
        ns->state = 1;
-       event_assign(&ns->event, ns->base->event_base, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
-       if (event_add(&ns->event, NULL) < 0) {
+       event_assign(&ns->event, ns->base->event_base, ns->socket,
+                                EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
+       if (!base->disable_when_inactive && event_add(&ns->event, NULL) < 0) {
                err = 2;
                goto out2;
        }
@@ -2768,7 +2791,10 @@ request_submit(struct request *const req) {
                /* if it has a nameserver assigned then this is going */
                /* straight into the inflight queue */
                evdns_request_insert(req, &REQ_HEAD(base, req->trans_id));
+
                base->global_requests_inflight++;
+               req->ns->requests_inflight++;
+
                evdns_request_transmit(req);
        } else {
                evdns_request_insert(req, &base->req_waiting_head);
@@ -3829,7 +3855,7 @@ evdns_config_windows_nameservers(void)
 #endif
 
 struct evdns_base *
-evdns_base_new(struct event_base *event_base, int initialize_nameservers)
+evdns_base_new(struct event_base *event_base, int flags)
 {
        struct evdns_base *base;
 
@@ -3877,7 +3903,16 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
 
        TAILQ_INIT(&base->hostsdb);
 
-       if (initialize_nameservers) {
+#define EVDNS_BASE_ALL_FLAGS (0x8001)
+       if (flags & ~EVDNS_BASE_ALL_FLAGS) {
+               flags = EVDNS_BASE_INITIALIZE_NAMESERVERS;
+               log(EVDNS_LOG_WARN,
+                   "Unrecognized flag passed to evdns_base_new(). Assuming "
+                   "you meant EVDNS_BASE_INITIALIZE_NAMESERVERS.");
+       }
+#undef EVDNS_BASE_ALL_FLAGS
+
+       if (flags & EVDNS_BASE_INITIALIZE_NAMESERVERS) {
                int r;
 #ifdef _WIN32
                r = evdns_base_config_windows_nameservers(base);
@@ -3889,6 +3924,10 @@ evdns_base_new(struct event_base *event_base, int initialize_nameservers)
                        return NULL;
                }
        }
+       if (flags & EVDNS_BASE_DISABLE_WHEN_INACTIVE) {
+               base->disable_when_inactive = 1;
+       }
+
        EVDNS_UNLOCK(base);
        return base;
 }
index e4accf21e63a250192d3dabca1d1f27024253f89..edd2a23ec934be2fa58b9babc783a5678ebc8f50 100644 (file)
@@ -201,6 +201,12 @@ typedef void (*evdns_callback_type) (int result, char type, int count, int ttl,
 struct evdns_base;
 struct event_base;
 
+/** Flag for evdns_base_new: process resolv.conf.  */
+#define EVDNS_BASE_INITIALIZE_NAMESERVERS 1
+/** Flag for evdns_base_new: Do not prevent the libevent event loop from
+ * exiting when we have no active dns requests. */
+#define EVDNS_BASE_DISABLE_WHEN_INACTIVE 0x8000
+
 /**
   Initialize the asynchronous DNS library.
 
@@ -209,7 +215,8 @@ struct event_base;
   evdns_config_windows_nameservers() on Windows.
 
   @param event_base the event base to associate the dns client with
-  @param initialize_nameservers 1 if resolve.conf processing should occur
+  @param flags any of EVDNS_BASE_INITIALIZE_NAMESERVERS|
+    EVDNS_BASE_DISABLE_WHEN_INACTIVE
   @return evdns_base object if successful, or NULL if an error occurred.
   @see evdns_base_free()
  */
index 84988554d7837abb6be026bc1804ce125b8c3e90..c4fb64a5eba4e46acb342214f833c1f64d251b11 100644 (file)
@@ -179,7 +179,7 @@ main(int c, char **v) {
 #endif
 
        event_base = event_base_new();
-       evdns_base = evdns_base_new(event_base, 0);
+       evdns_base = evdns_base_new(event_base, EVDNS_BASE_DISABLE_WHEN_INACTIVE);
        evdns_set_log_fn(logfn);
 
        if (servertest) {