Subject: Patch for epoll/kqeueue/devpoll-style interaction

Patch for epoll/kqeueue/devpoll-style interaction

From: Nick Mathewson <nickm_at_freehaven.net>
Date: 2006-04-14

Hi! I'm glad to see that c-ares is being decoupled from select().
Unfortunately, the new ares_getsock interface is not particularly
well-suited for high-performance async io implementations such as
epoll (Linux), kqueue (BSD) and /dev/poll (SunOS, I think). Rather
than repeatedly building a list of sockets, for these interfaces, it
is most convenient to know when a socket's state has changed.

This patch introduces a callback mechanism that gets invoked when a
socket becomes (or stops being) interested in reads or writes; it
would make c-ares way easier to use for my application
(http://tor.eff.org/).

Thanks for your time!

Index: ares.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares.h,v
retrieving revision 1.22
diff -u -r1.22 ares.h
--- ares.h 22 Dec 2005 15:27:41 -0000 1.22
+++ ares.h 14 Apr 2006 07:45:46 -0000
@@ -93,6 +93,7 @@
 #define ARES_OPT_SERVERS (1 << 6)
 #define ARES_OPT_DOMAINS (1 << 7)
 #define ARES_OPT_LOOKUPS (1 << 8)
+#define ARES_OPT_SOCK_STATE_CB (1 << 9)
 
 /* Nameinfo flag values */
 #define ARES_NI_NOFQDN (1 << 0)
@@ -135,6 +136,18 @@
 #define ARES_GETSOCK_WRITABLE(bits,num) (bits & (1 << ((num) + \
                                          ARES_GETSOCK_MAXNUM)))
 
+#ifdef WIN32
+typedef void (*ares_sock_state_cb)(void *data,
+ SOCKET socket,
+ int readable,
+ int writable);
+#else
+typedef void (*ares_sock_state_cb)(void *data,
+ int socket,
+ int readable,
+ int writable);
+#endif
+
 struct ares_options {
   int flags;
   int timeout;
@@ -147,6 +160,8 @@
   char **domains;
   int ndomains;
   char *lookups;
+ ares_sock_state_cb sock_state_cb;
+ void *sock_state_cb_data;
 };
 
 struct hostent;
Index: ares__close_sockets.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares__close_sockets.c,v
retrieving revision 1.4
diff -u -r1.4 ares__close_sockets.c
--- ares__close_sockets.c 20 Aug 2004 13:45:26 -0000 1.4
+++ ares__close_sockets.c 14 Apr 2006 07:45:46 -0000
@@ -23,7 +23,7 @@
 #include "ares.h"
 #include "ares_private.h"
 
-void ares__close_sockets(struct server_state *server)
+void ares__close_sockets(ares_channel channel, struct server_state *server)
 {
   struct send_request *sendreq;
 
@@ -46,11 +46,13 @@
   /* Close the TCP and UDP sockets. */
   if (server->tcp_socket != ARES_SOCKET_BAD)
     {
+ SOCK_STATE_CALLBACK(channel, server->tcp_socket, 0, 0);
       closesocket(server->tcp_socket);
       server->tcp_socket = ARES_SOCKET_BAD;
     }
   if (server->udp_socket != ARES_SOCKET_BAD)
     {
+ SOCK_STATE_CALLBACK(channel, server->udp_socket, 0, 0);
       closesocket(server->udp_socket);
       server->udp_socket = ARES_SOCKET_BAD;
     }
Index: ares_cancel.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_cancel.c,v
retrieving revision 1.2
diff -u -r1.2 ares_cancel.c
--- ares_cancel.c 22 Jul 2004 22:18:45 -0000 1.2
+++ ares_cancel.c 14 Apr 2006 07:45:46 -0000
@@ -38,6 +38,6 @@
   if (!(channel->flags & ARES_FLAG_STAYOPEN))
   {
     for (i = 0; i < channel->nservers; i++)
- ares__close_sockets(&channel->servers[i]);
+ ares__close_sockets(channel, &channel->servers[i]);
   }
 }
Index: ares_destroy.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_destroy.c,v
retrieving revision 1.4
diff -u -r1.4 ares_destroy.c
--- ares_destroy.c 22 Jul 2004 22:18:45 -0000 1.4
+++ ares_destroy.c 14 Apr 2006 07:45:46 -0000
@@ -24,7 +24,7 @@
   struct query *query;
 
   for (i = 0; i < channel->nservers; i++)
- ares__close_sockets(&channel->servers[i]);
+ ares__close_sockets(channel, &channel->servers[i]);
   free(channel->servers);
   for (i = 0; i < channel->ndomains; i++)
     free(channel->domains[i]);
Index: ares_init.3
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_init.3,v
retrieving revision 1.2
diff -u -r1.2 ares_init.3
--- ares_init.3 7 Dec 2004 10:43:21 -0000 1.2
+++ ares_init.3 14 Apr 2006 07:45:46 -0000
@@ -98,6 +98,24 @@
 .I lookups
 should be set to a string of the characters "b" or "f", where "b"
 indicates a DNS lookup and "f" indicates a lookup in the hosts file.
+.TP 18
+.B ARES_OPT_SOCK_STATE_CB
+.B void (*\fIsock_state_cb\fP)(void *data, int s, int read, int write);
+.br
+.B void *\fIsock_state_cb_data\fP;
+.br
+A callback function to be invoked when a socket changes state.
+.I s
+will be passed the socket whose state has changed;
+.I read
+will be set to true if the socket should listen for read events, and
+.I write
+will be set to true if the socket should listen for write events.
+The value of
+.I sock_state_cb_data
+will be passed as the
+.I data
+argument.
 .PP
 The
 .I flags
Index: ares_init.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_init.c,v
retrieving revision 1.35
diff -u -r1.35 ares_init.c
--- ares_init.c 5 Jan 2006 07:57:32 -0000 1.35
+++ ares_init.c 14 Apr 2006 07:45:46 -0000
@@ -113,6 +113,7 @@
   channel->queries = NULL;
   channel->domains = NULL;
   channel->sortlist = NULL;
+ channel->sock_state_cb = NULL;
 
   /* Initialize configuration by each of the four sources, from highest
    * precedence to lowest.
@@ -192,6 +193,11 @@
     channel->udp_port = options->udp_port;
   if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1)
     channel->tcp_port = options->tcp_port;
+ if ((optmask & ARES_OPT_SOCK_STATE_CB) && channel->sock_state_cb == NULL)
+ {
+ channel->sock_state_cb = options->sock_state_cb;
+ channel->sock_state_cb_data = options->sock_state_cb_data;
+ }
 
   /* Copy the servers, if given. */
   if ((optmask & ARES_OPT_SERVERS) && channel->nservers == -1)
Index: ares_private.h
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_private.h,v
retrieving revision 1.20
diff -u -r1.20 ares_private.h
--- ares_private.h 5 Jan 2006 07:57:32 -0000 1.20
+++ ares_private.h 14 Apr 2006 07:45:46 -0000
@@ -176,13 +176,22 @@
 
   /* Active queries */
   struct query *queries;
+
+ ares_sock_state_cb sock_state_cb;
+ void *sock_state_cb_data;
 };
 
 void ares__send_query(ares_channel channel, struct query *query, time_t now);
-void ares__close_sockets(struct server_state *server);
+void ares__close_sockets(ares_channel channel, struct server_state *server);
 int ares__get_hostent(FILE *fp, int family, struct hostent **host);
 int ares__read_line(FILE *fp, char **buf, int *bufsize);
 
+#define SOCK_STATE_CALLBACK(c, s, r, w) \
+ do { \
+ if ((c)->sock_state_cb) \
+ (c)->sock_state_cb((c)->sock_state_cb_data, (s), (r), (w)); \
+ } while (0)
+
 #ifdef CURLDEBUG
 /* This is low-level hard-hacking memory leak tracking and similar. Using the
    libcurl lowlevel code from within library is ugly and only works when
Index: ares_process.c
===================================================================
RCS file: /cvsroot/curl/curl/ares/ares_process.c,v
retrieving revision 1.24
diff -u -r1.24 ares_process.c
--- ares_process.c 20 Dec 2005 20:48:38 -0000 1.24
+++ ares_process.c 14 Apr 2006 07:45:46 -0000
@@ -149,7 +149,10 @@
                   wcount -= sendreq->len;
                   server->qhead = sendreq->next;
                   if (server->qhead == NULL)
- server->qtail = NULL;
+ {
+ SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
+ server->qtail = NULL;
+ }
                   free(sendreq);
                 }
               else
@@ -179,7 +182,10 @@
             {
               server->qhead = sendreq->next;
               if (server->qhead == NULL)
- server->qtail = NULL;
+ {
+ SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 0);
+ server->qtail = NULL;
+ }
               free(sendreq);
             }
           else
@@ -380,7 +386,7 @@
   struct query *query, *next;
 
   /* Reset communications with this server. */
- ares__close_sockets(&channel->servers[whichserver]);
+ ares__close_sockets(channel, &channel->servers[whichserver]);
 
   /* Tell all queries talking to this server to move on and not try
    * this server again.
@@ -452,7 +458,10 @@
       if (server->qtail)
         server->qtail->next = sendreq;
       else
- server->qhead = sendreq;
+ {
+ SOCK_STATE_CALLBACK(channel, server->tcp_socket, 1, 1);
+ server->qhead = sendreq;
+ }
       server->qtail = sendreq;
       query->timeout = 0;
     }
@@ -575,6 +584,7 @@
     }
   }
 
+ SOCK_STATE_CALLBACK(channel, s, 1, 0);
   server->tcp_buffer_pos = 0;
   server->tcp_socket = s;
   return 0;
@@ -604,6 +614,8 @@
       return -1;
     }
 
+ SOCK_STATE_CALLBACK(channel, s, 1, 0);
+
   server->udp_socket = s;
   return 0;
 }
@@ -714,7 +726,7 @@
   if (!channel->queries && !(channel->flags & ARES_FLAG_STAYOPEN))
     {
       for (i = 0; i < channel->nservers; i++)
- ares__close_sockets(&channel->servers[i]);
+ ares__close_sockets(channel, &channel->servers[i]);
     }
   return (next);
 }

Received on Fri Apr 14 09:55:00 2006