Subject: Re: [PATCH] save ares channel options for caching purposes

Re: [PATCH] save ares channel options for caching purposes

From: Brad House <brad_at_mainstreetsoftworks.com>
Date: Tue, 15 May 2007 15:37:07 -0400

> Problem: Calling ares_init() for each lookup can be unnecessarily
> resource intensive. On windows, it must LoadLibrary()
> or search the registry on each call to ares_init(). On
> unix, it must read and parse multiple files to obtain the
> necessary configuration information. In a single-threaded
> environment, it would make sense to only ares_init() once,
> but in a heavily multi-threaded environment, it is undesirable
> to ares_init() and ares_destroy() for each thread created and
> track that.
>
> Solution: Create ares_save_options() and ares_destroy_options() functions
> to retrieve and free options obtained from an initialized channel.
> The options populated can be used to pass back into ares_init_options(),
> it should populate all needed fields and not retrieve any information
> from the system. Probably wise to destroy the cache every minute or
> so to prevent the data from becoming stale.
>
> Basic usage:
> {
> ares_channel ch;
> struct ares_options opt;
> int optmask;
>
> /* Save config options */
> ares_init(&ch);
> ares_save_options(ch, &opt, &optmask);
> ares_destroy(ch);
>
> /* Reload saved configs */
> ares_init_options(&ch, &opt, optmask);
> ...
> ares_destroy(ch);
>
> /* Cleanup */
> ares_destroy_options(&opt);
> }
>
>
> Patch Notes:
> * Had to add an ARES_CONFIG_CHECK() macro which gets called within
> init_by_resolv_conf() to force Unix systems to not re-read the
> resolv.conf and friends.
> * Tagged the 'options' parameter for init_by_options() and
> ares_init_options() as const since they are not modified by
> those functions.
> * If ares_save_options() returns anything other than ARES_SUCCESS,
> you need to call ares_destroy_options() to ensure any associated
> memory is free()'d.
> * Sortlist support was added to the options structure and
> init_by_options() function.
>
> Patch is attached. Questions, Concerns, Thoughts for improvement are
> all appreciated.

Totally screwed up on the memset() at the top of ares_save_options(), sorry
about that. Attached is the latest, _working_, version.

Thanks.
-Brad

Index: monetra/trunk/monetra/c-ares/ares_destroy.c
===================================================================
--- monetra/trunk/monetra/c-ares/ares_destroy.c (revision 10830)
+++ monetra/trunk/monetra/c-ares/ares_destroy.c (revision 10908)
@@ -20,4 +20,17 @@
 #include "ares.h"
 #include "ares_private.h"
+
+void ares_destroy_options(struct ares_options *options)
+{
+ int i;
+
+ free(options->servers);
+ for (i = 0; i < options->ndomains; i++)
+ free(options->domains[i]);
+ free(options->domains);
+ if(options->sortlist)
+ free(options->sortlist);
+ free(options->lookups);
+}
 
 void ares_destroy(ares_channel channel)
Index: monetra/trunk/monetra/c-ares/ares.h
===================================================================
--- monetra/trunk/monetra/c-ares/ares.h (revision 10830)
+++ monetra/trunk/monetra/c-ares/ares.h (revision 10908)
@@ -95,4 +95,5 @@
 #define ARES_OPT_LOOKUPS (1 << 8)
 #define ARES_OPT_SOCK_STATE_CB (1 << 9)
+#define ARES_OPT_SORTLIST (1 << 10)
 
 /* Nameinfo flag values */
@@ -149,4 +150,6 @@
 #endif
 
+struct apattern;
+
 struct ares_options {
   int flags;
@@ -163,4 +166,6 @@
   ares_sock_state_cb sock_state_cb;
   void *sock_state_cb_data;
+ struct apattern *sortlist;
+ int nsort;
 };
 
@@ -178,6 +183,8 @@
 
 int ares_init(ares_channel *channelptr);
-int ares_init_options(ares_channel *channelptr, struct ares_options *options,
+int ares_init_options(ares_channel *channelptr, const struct ares_options *options,
                       int optmask);
+int ares_save_options(ares_channel channel, struct ares_options *options, int *optmask);
+void ares_destroy_options(struct ares_options *options);
 void ares_destroy(ares_channel channel);
 void ares_cancel(ares_channel channel);
Index: monetra/trunk/monetra/c-ares/ares_init.c
===================================================================
--- monetra/trunk/monetra/c-ares/ares_init.c (revision 10830)
+++ monetra/trunk/monetra/c-ares/ares_init.c (revision 10941)
@@ -60,5 +60,5 @@
 #endif
 
-static int init_by_options(ares_channel channel, struct ares_options *options,
+static int init_by_options(ares_channel channel, const struct ares_options *options,
                            int optmask);
 static int init_by_environment(ares_channel channel);
@@ -83,4 +83,9 @@
 #endif
 
+#define ARES_CONFIG_CHECK() (channel->lookups && channel->nsort > -1 && \
+ channel->nservers > -1 && channel->ndomains > -1 && \
+ channel->ndots > -1 && channel->timeout > -1 && \
+ channel->tries > -1)
+
 int ares_init(ares_channel *channelptr)
 {
@@ -88,5 +93,5 @@
 }
 
-int ares_init_options(ares_channel *channelptr, struct ares_options *options,
+int ares_init_options(ares_channel *channelptr, const struct ares_options *options,
                       int optmask)
 {
@@ -188,5 +193,70 @@
 }
 
-static int init_by_options(ares_channel channel, struct ares_options *options,
+/* Save options from initialized channel */
+int ares_save_options(ares_channel channel, struct ares_options *options, int *optmask)
+{
+ int i;
+
+ /* Zero everything out */
+ memset(options, 0, sizeof(struct ares_options));
+
+ if (!ARES_CONFIG_CHECK())
+ return ARES_ENODATA;
+
+ (*optmask) = (ARES_OPT_FLAGS|ARES_OPT_TIMEOUT|ARES_OPT_TRIES|ARES_OPT_NDOTS|
+ ARES_OPT_UDP_PORT|ARES_OPT_TCP_PORT|ARES_OPT_SOCK_STATE_CB|
+ ARES_OPT_SERVERS|ARES_OPT_DOMAINS|ARES_OPT_LOOKUPS|ARES_OPT_SORTLIST);
+
+ /* Copy easy stuff */
+ options->flags = channel->flags;
+ options->timeout = channel->timeout;
+ options->tries = channel->tries;
+ options->ndots = channel->ndots;
+ options->udp_port = channel->udp_port;
+ options->tcp_port = channel->tcp_port;
+ options->sock_state_cb = channel->sock_state_cb;
+ options->sock_state_cb_data = channel->sock_state_cb_data;
+
+ /* Copy servers */
+ options->servers =
+ malloc(channel->nservers * sizeof(struct server_state));
+ if (!options->servers && channel->nservers != 0)
+ return ARES_ENOMEM;
+ for (i = 0; i < channel->nservers; i++)
+ options->servers[i] = channel->servers[i].addr;
+ options->nservers = channel->nservers;
+
+ /* copy domains */
+ options->domains = malloc(channel->ndomains * sizeof(char *));
+ if (!options->domains)
+ return ARES_ENOMEM;
+ for (i = 0; i < channel->ndomains; i++)
+ {
+ options->ndomains = i;
+ options->domains[i] = strdup(channel->domains[i]);
+ if (!options->domains[i])
+ return ARES_ENOMEM;
+ }
+ options->ndomains = channel->ndomains;
+
+ /* copy lookups */
+ options->lookups = strdup(channel->lookups);
+ if (!options->lookups)
+ return ARES_ENOMEM;
+
+ /* copy sortlist */
+ options->sortlist = malloc(channel->nsort * sizeof(struct apattern));
+ if (!options->sortlist)
+ return ARES_ENOMEM;
+ for (i = 0; i < channel->nsort; i++)
+ {
+ memcpy(&(options->sortlist[i]), &(channel->sortlist[i]), sizeof(struct apattern));
+ }
+ options->nsort = channel->nsort;
+
+ return ARES_SUCCESS;
+}
+
+static int init_by_options(ares_channel channel, const struct ares_options *options,
                            int optmask)
 {
@@ -248,4 +318,17 @@
       if (!channel->lookups)
         return ARES_ENOMEM;
+ }
+
+ /* copy sortlist */
+ if ((optmask & ARES_OPT_SORTLIST) && channel->nsort == -1)
+ {
+ channel->sortlist = malloc(options->nsort * sizeof(struct apattern));
+ if (!channel->sortlist)
+ return ARES_ENOMEM;
+ for (i = 0; i < options->nsort; i++)
+ {
+ memcpy(&(channel->sortlist[i]), &(options->sortlist[i]), sizeof(struct apattern));
+ }
+ channel->nsort = options->nsort;
     }
 
@@ -578,4 +661,8 @@
     int linesize;
 
+ /* Don't read resolv.conf and friends if we don't have to */
+ if (ARES_CONFIG_CHECK())
+ return ARES_SUCCESS;
+
     fp = fopen(PATH_RESOLV_CONF, "r");
     if (!fp)
Received on 2007-05-15