From fd79189d384638c2f7086d29ab2a177fbb6686c7 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 12 Feb 2014 16:13:32 +0000 Subject: [PATCH] Introduce new refcounts infrastructure. --- include/rdns.h | 8 +++--- src/dns_private.h | 6 ++-- src/ref.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++ src/resolver.c | 59 ++++++++++++++++++++------------------ src/util.c | 58 ++++++++++++++++--------------------- src/util.h | 25 ++++++---------- test/dns_curve.c | 4 +-- test/dns_regress.c | 7 +++-- 8 files changed, 150 insertions(+), 88 deletions(-) create mode 100644 src/ref.h diff --git a/include/rdns.h b/include/rdns.h index a4043fe..2486c1e 100644 --- a/include/rdns.h +++ b/include/rdns.h @@ -190,10 +190,10 @@ void rdns_resolver_register_plugin (struct rdns_resolver *resolver, bool rdns_resolver_init (struct rdns_resolver *resolver); /** - * Destroy resolver and free all associated structures + * Decrease refcount for a resolver and free it if refcount is 0 * @param resolver */ -void rdns_resolver_destroy (struct rdns_resolver *resolver); +void rdns_resolver_release (struct rdns_resolver *resolver); /** * Make a DNS request @@ -232,13 +232,13 @@ const char *rdns_strtype (enum rdns_request_type type); * @param req * @return */ -struct rdns_request* rdns_request_ref (struct rdns_request *req); +struct rdns_request* rdns_request_retain (struct rdns_request *req); /** * Decrease refcount for a request and free it if refcount is 0 * @param req */ -void rdns_request_unref (struct rdns_request *req); +void rdns_request_release (struct rdns_request *req); /* * Private functions used by async libraries as callbacks diff --git a/src/dns_private.h b/src/dns_private.h index 5c1bf3d..622bc14 100644 --- a/src/dns_private.h +++ b/src/dns_private.h @@ -28,6 +28,7 @@ #include "utlist.h" #include "rdns.h" #include "upstream.h" +#include "ref.h" #define DNS_DEBUG(...) do { fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0); @@ -78,7 +79,6 @@ struct rdns_request { int id; const char *requested_name; - int ref; enum { RDNS_REQUEST_NEW = 0, RDNS_REQUEST_REGISTERED = 1, @@ -98,6 +98,7 @@ struct rdns_request { void *network_plugin_data; UT_hash_handle hh; + ref_entry_t ref; }; /** @@ -110,9 +111,9 @@ struct rdns_io_channel { void *async_io; /** async opaque ptr */ struct rdns_request *requests; /**< requests in flight */ struct rdns_io_channel *prev, *next; - unsigned int ref; bool want_reinit; UT_hash_handle hh; + ref_entry_t ref; }; @@ -126,6 +127,7 @@ struct rdns_resolver { bool async_binded; bool initialized; + ref_entry_t ref; }; struct dns_header; diff --git a/src/ref.h b/src/ref.h new file mode 100644 index 0000000..ac9f8da --- /dev/null +++ b/src/ref.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2014, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef REF_H_ +#define REF_H_ + +/** + * @file ref.h + * A set of macros to handle refcounts + */ + +typedef void (*ref_dtor_cb_t)(void *data); + +typedef struct ref_entry_s { + unsigned int refcount; + ref_dtor_cb_t dtor; +} ref_entry_t; + +#define REF_INIT(obj, dtor_cb) do { \ + (obj)->ref.refcount = 0; \ + (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \ +} while (0) + +#define REF_INIT_RETAIN(obj, dtor_cb) do { \ + (obj)->ref.refcount = 1; \ + (obj)->ref.dtor = (ref_dtor_cb_t)(dtor_cb); \ +} while (0) + +#ifdef HAVE_ATOMIC +#define REF_RETAIN(obj) do { \ + __sync_add_and_fetch (&(obj)->ref.refcount, 1); \ +} while (0) + +#define REF_RELEASE(obj) do { \ + unsigned int rc = __sync_sub_and_fetch (&(obj)->ref.refcount, 1) \ + if (rc == 0 && (obj)->ref.dtor) { \ + (obj)->ref.dtor (obj); \ + } \ +} while (0) +#else +#define REF_RETAIN(obj) do { \ + (obj)->ref.refcount ++; \ +} while (0) + +#define REF_RELEASE(obj) do { \ + if (--(obj)->ref.refcount == 0 && (obj)->ref.dtor) { \ + (obj)->ref.dtor (obj); \ + } \ +} while (0) +#endif + +#endif /* REF_H_ */ diff --git a/src/resolver.c b/src/resolver.c index fb37ef5..f8fd472 100644 --- a/src/resolver.c +++ b/src/resolver.c @@ -268,9 +268,9 @@ rdns_process_read (int fd, void *arg) if (req != NULL) { if (rdns_parse_reply (in, r, req, &rep)) { UPSTREAM_OK (req->io->srv); - rdns_request_ref (req); + REF_RETAIN (req); req->func (rep, req->arg); - rdns_request_unref (req); + REF_RELEASE (req); } } } @@ -289,9 +289,9 @@ rdns_process_timer (void *arg) if (req->retransmits == 0) { UPSTREAM_FAIL (req->io->srv, time (NULL)); rep = rdns_make_reply (req, DNS_RC_TIMEOUT); - rdns_request_ref (req); + REF_RETAIN (req); req->func (rep, req->arg); - rdns_request_unref (req); + REF_RELEASE (req); return; } @@ -308,9 +308,9 @@ rdns_process_timer (void *arg) else if (r == -1) { UPSTREAM_FAIL (req->io->srv, time (NULL)); rep = rdns_make_reply (req, DNS_RC_NETERR); - rdns_request_ref (req); + REF_RETAIN (req); req->func (rep, req->arg); - rdns_request_unref (req); + REF_RELEASE (req); } else { req->async->repeat_timer (req->async->data, req->async_event); @@ -349,9 +349,9 @@ rdns_process_retransmit (int fd, void *arg) else if (r == -1) { UPSTREAM_FAIL (req->io->srv, time (NULL)); rep = rdns_make_reply (req, DNS_RC_NETERR); - rdns_request_ref (req); + REF_RETAIN (req); req->func (rep, req->arg); - rdns_request_unref (req); + REF_RELEASE (req); } else { req->async_event = req->async->add_timer (req->async->data, @@ -391,9 +391,9 @@ rdns_make_request_full ( req->resolver = resolver; req->func = cb; req->arg = cbdata; - req->ref = 1; req->reply = NULL; req->network_plugin_data = NULL; + REF_INIT_RETAIN (req, rdns_request_free); va_start (args, queries); for (i = 0; i < queries; i ++) { @@ -437,7 +437,7 @@ rdns_make_request_full ( if (serv == NULL) { DNS_DEBUG ("cannot find suitable server for request"); - rdns_request_unref (req); + REF_RELEASE (req); return NULL; } @@ -446,7 +446,7 @@ rdns_make_request_full ( req->io = serv->cur_io_channel; if (req->io == NULL) { DNS_DEBUG ("cannot find suitable io channel for the server %s", serv->name); - rdns_request_unref (req); + REF_RELEASE (req); return NULL; } serv->cur_io_channel = serv->cur_io_channel->next; @@ -455,11 +455,12 @@ rdns_make_request_full ( r = rdns_send_request (req, req->io->sock, true); if (r == -1) { - rdns_request_unref (req); + REF_RELEASE (req); return NULL; } - rdns_ioc_ref (req->io); + REF_RETAIN (req->io); + REF_RETAIN (req->resolver); return req; } @@ -488,10 +489,10 @@ rdns_resolver_init (struct rdns_resolver *resolver) ioc->resolver = resolver; ioc->async_io = resolver->async->add_read (resolver->async->data, ioc->sock, ioc); - ioc->ref = 1; serv->cur_io_channel = ioc; CDL_PREPEND (serv->io_channels, ioc); HASH_ADD_INT (resolver->io_channels, sock, ioc); + REF_INIT_RETAIN (ioc, rdns_ioc_free); } } } @@ -560,18 +561,8 @@ rdns_resolver_add_server (struct rdns_resolver *resolver, return true; } -struct rdns_resolver * -rdns_resolver_new (void) -{ - struct rdns_resolver *new; - - new = calloc (1, sizeof (struct rdns_resolver)); - - return new; -} - -void -rdns_resolver_destroy (struct rdns_resolver *resolver) +static void +rdns_resolver_free (struct rdns_resolver *resolver) { struct rdns_server *serv, *stmp; struct rdns_io_channel *ioc, *itmp1, *itmp2; @@ -587,16 +578,30 @@ rdns_resolver_destroy (struct rdns_resolver *resolver) UPSTREAM_FOREACH_SAFE (resolver->servers, serv, stmp) { CDL_FOREACH_SAFE (serv->io_channels, ioc, itmp1, itmp2) { HASH_DELETE (hh, resolver->io_channels, ioc); - rdns_ioc_unref (ioc, resolver->async); + REF_RELEASE (ioc); } UPSTREAM_DEL (resolver->servers, serv); free (serv->name); free (serv); } } + free (resolver->async); free (resolver); } + +struct rdns_resolver * +rdns_resolver_new (void) +{ + struct rdns_resolver *new; + + new = calloc (1, sizeof (struct rdns_resolver)); + + REF_INIT_RETAIN (new, rdns_resolver_free); + + return new; +} + void rdns_resolver_async_bind (struct rdns_resolver *resolver, struct rdns_async_context *ctx) diff --git a/src/util.c b/src/util.c index 86b3a8e..cbdb9e1 100644 --- a/src/util.c +++ b/src/util.c @@ -257,7 +257,7 @@ rdns_permutor_generate_id (void) } -static void +void rdns_reply_free (struct rdns_reply *rep) { struct rdns_reply_entry *entry, *tmp; @@ -283,15 +283,10 @@ rdns_reply_free (struct rdns_reply *rep) free (rep); } -static void +void rdns_request_free (struct rdns_request *req) { if (req != NULL) { - if (req->io != NULL && req->state > RDNS_REQUEST_NEW) { - /* Remove from id hashes */ - HASH_DEL (req->io->requests, req); - rdns_ioc_unref (req->io, req->async); - } if (req->packet != NULL) { free (req->packet); } @@ -312,50 +307,47 @@ rdns_request_free (struct rdns_request *req) req->resolver->network_plugin->cb.network_plugin.finish_cb ( req, req->resolver->network_plugin->data); } + if (req->io != NULL && req->state > RDNS_REQUEST_NEW) { + /* Remove from id hashes */ + HASH_DEL (req->io->requests, req); + REF_RELEASE (req->io); + REF_RELEASE (req->resolver); + } + free (req); } } -struct rdns_request* -rdns_request_ref (struct rdns_request *req) -{ - req->ref ++; - return req; -} - void -rdns_request_unref (struct rdns_request *req) -{ - if (--req->ref <= 0) { - rdns_request_free (req); - } -} - -static void -rdns_ioc_free (struct rdns_io_channel *ioc, struct rdns_async_context *async) +rdns_ioc_free (struct rdns_io_channel *ioc) { struct rdns_request *req, *rtmp; HASH_ITER (hh, ioc->requests, req, rtmp) { HASH_DELETE (hh, ioc->requests, req); - rdns_request_unref (req); + REF_RELEASE (req); } - async->del_read (async->data, ioc->async_io); + ioc->resolver->async->del_read (ioc->resolver->async->data, + ioc->async_io); close (ioc->sock); free (ioc); } -struct rdns_io_channel * -rdns_ioc_ref (struct rdns_io_channel *ioc) +void +rdns_resolver_release (struct rdns_resolver *resolver) { - ioc->ref ++; - return ioc; + REF_RELEASE (resolver); +} + +struct rdns_request* +rdns_request_retain (struct rdns_request *req) +{ + REF_RETAIN (req); + return req; } void -rdns_ioc_unref (struct rdns_io_channel *ioc, struct rdns_async_context *async) +rdns_request_release (struct rdns_request *req) { - if (--ioc->ref <= 0) { - rdns_ioc_free (ioc, async); - } + REF_RELEASE (req); } diff --git a/src/util.h b/src/util.h index 20a0b8e..035f49a 100644 --- a/src/util.h +++ b/src/util.h @@ -41,31 +41,22 @@ rdns_make_client_socket (const char *credits, uint16_t port, */ uint16_t rdns_permutor_generate_id (void); -/** - * Increase refcount in the request - * @param req - * @return - */ -struct rdns_request* rdns_request_ref (struct rdns_request *req); /** - * Decrease refcount in the request and free it if refcount is zero - * @param req + * Free IO channel */ -void rdns_request_unref (struct rdns_request *req); +void rdns_ioc_free (struct rdns_io_channel *ioc); /** - * Increase refcount for an IO channel - * @param ioc - * @return + * Free request + * @param req */ -struct rdns_io_channel * rdns_ioc_ref (struct rdns_io_channel *ioc); +void rdns_request_free (struct rdns_request *req); /** - * Decrease refcount for an IO channel and free it if refcount is zero - * @param ioc - * @param async async object from the resolver + * Free reply + * @param rep */ -void rdns_ioc_unref (struct rdns_io_channel *ioc, struct rdns_async_context *async); +void rdns_reply_free (struct rdns_reply *rep); #endif /* UTIL_H_ */ diff --git a/test/dns_curve.c b/test/dns_curve.c index 1088f40..95166e5 100644 --- a/test/dns_curve.c +++ b/test/dns_curve.c @@ -34,10 +34,10 @@ static void rdns_regress_callback (struct rdns_reply *reply, void *arg) { printf ("got result for host: %s\n", (const char *)arg); - rdns_request_unref (reply->request); + rdns_request_release (reply->request); if (--remain_tests == 0) { - rdns_resolver_destroy (reply->resolver); + rdns_resolver_release (reply->resolver); } } diff --git a/test/dns_regress.c b/test/dns_regress.c index 63e1afe..2f89981 100644 --- a/test/dns_regress.c +++ b/test/dns_regress.c @@ -32,10 +32,10 @@ static void rdns_regress_callback (struct rdns_reply *reply, void *arg) { printf ("got result for host: %s\n", (const char *)arg); - rdns_request_unref (reply->request); + rdns_request_release (reply->request); if (--remain_tests == 0) { - rdns_resolver_destroy (reply->resolver); + rdns_resolver_release (reply->resolver); } } @@ -84,10 +84,11 @@ main (int argc, char **argv) rdns_test_a (resolver_ev); ev_loop (loop, 0); + ev_loop_destroy (loop); rdns_test_a (resolver_event); event_base_loop (base, 0); - + event_base_free (base); return 0; }