]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
pullup:
authorMark Andrews <marka@isc.org>
Mon, 17 Feb 2003 05:32:17 +0000 (05:32 +0000)
committerMark Andrews <marka@isc.org>
Mon, 17 Feb 2003 05:32:17 +0000 (05:32 +0000)
1427.   [bug]           Race condition in adb with threaded build.

CHANGES
lib/dns/adb.c

diff --git a/CHANGES b/CHANGES
index 9c0bae6255bb27fd3635a926759b6e6656c95639..b53a00c692e70448774bbaa6788508b9b6152076 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,7 @@
+1427.  [bug]           Race condition in adb with threaded build.
 
 1426.   [cleanup]      Disable RFC2535 style DNSSEC.  This is incompatible
-                       with the forth coming DS style DNSSEC.
+                       with the forthcoming DS style DNSSEC.
 
 1382.  [bug]           make install failed with --enable-libbind. [RT #3656]
 
index 85612d4b3bd9cb6c18a30d66f36888ae331094bd..51de22d7837b442e4772bf64f8785779fd020eb8 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: adb.c,v 1.181.2.4 2002/08/05 06:57:11 marka Exp $ */
+/* $Id: adb.c,v 1.181.2.4.4.1 2003/02/17 05:32:17 marka Exp $ */
 
 /*
  * Implementation notes
@@ -279,7 +279,7 @@ static inline void free_adbzoneinfo(dns_adb_t *, dns_adbzoneinfo_t **);
 static inline dns_adbentry_t *new_adbentry(dns_adb_t *);
 static inline void free_adbentry(dns_adb_t *, dns_adbentry_t **);
 static inline dns_adbfind_t *new_adbfind(dns_adb_t *);
-static inline void free_adbfind(dns_adb_t *, dns_adbfind_t **);
+static inline isc_boolean_t free_adbfind(dns_adb_t *, dns_adbfind_t **);
 static inline dns_adbaddrinfo_t *new_adbaddrinfo(dns_adb_t *, dns_adbentry_t *,
                                                 in_port_t);
 static inline dns_adbfetch_t *new_adbfetch(dns_adb_t *);
@@ -298,18 +298,18 @@ static void print_namehook_list(FILE *, const char *legend,
                                isc_boolean_t debug);
 static void print_find_list(FILE *, dns_adbname_t *);
 static void print_fetch_list(FILE *, dns_adbname_t *);
-static inline void dec_adb_irefcnt(dns_adb_t *);
+static inline isc_boolean_t dec_adb_irefcnt(dns_adb_t *);
 static inline void inc_adb_erefcnt(dns_adb_t *);
 static inline void inc_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
                                    isc_boolean_t);
-static inline void dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
+static inline isc_boolean_t dec_entry_refcnt(dns_adb_t *, dns_adbentry_t *,
                                    isc_boolean_t);
 static inline void violate_locking_hierarchy(isc_mutex_t *, isc_mutex_t *);
-static void clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
+static isc_boolean_t clean_namehooks(dns_adb_t *, dns_adbnamehooklist_t *);
 static void clean_target(dns_adb_t *, dns_name_t *);
 static void clean_finds_at_name(dns_adbname_t *, isc_eventtype_t,
                                unsigned int);
-static void check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
+static isc_boolean_t check_expire_namehooks(dns_adbname_t *, isc_stdtime_t);
 static void cancel_fetches_at_name(dns_adbname_t *);
 static isc_result_t dbfind_name(dns_adbname_t *, isc_stdtime_t,
                                dns_rdatatype_t);
@@ -319,13 +319,13 @@ static isc_result_t fetch_name_a6(dns_adbname_t *, isc_boolean_t);
 static inline void check_exit(dns_adb_t *);
 static void timer_cleanup(isc_task_t *, isc_event_t *);
 static void destroy(dns_adb_t *);
-static void shutdown_names(dns_adb_t *);
-static void shutdown_entries(dns_adb_t *);
+static isc_boolean_t shutdown_names(dns_adb_t *);
+static isc_boolean_t shutdown_entries(dns_adb_t *);
 static inline void link_name(dns_adb_t *, int, dns_adbname_t *);
-static inline void unlink_name(dns_adb_t *, dns_adbname_t *);
+static inline isc_boolean_t unlink_name(dns_adb_t *, dns_adbname_t *);
 static inline void link_entry(dns_adb_t *, int, dns_adbentry_t *);
-static inline void unlink_entry(dns_adb_t *, dns_adbentry_t *);
-static void kill_name(dns_adbname_t **, isc_eventtype_t);
+static inline isc_boolean_t unlink_entry(dns_adb_t *, dns_adbentry_t *);
+static isc_boolean_t kill_name(dns_adbname_t **, isc_eventtype_t);
 static void fetch_callback_a6(isc_task_t *, isc_event_t *);
 static isc_result_t dbfind_a6(dns_adbname_t *, isc_stdtime_t);
 
@@ -671,9 +671,11 @@ import_a6(dns_a6context_t *a6ctx) {
 /*
  * Requires the name's bucket be locked.
  */
-static void
+static isc_boolean_t
 kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
        dns_adbname_t *name;
+       isc_boolean_t result = ISC_FALSE;
+       isc_boolean_t result4, result6;
        dns_adb_t *adb;
 
        INSIST(n != NULL);
@@ -690,9 +692,11 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
         * away now or not.
         */
        if (NAME_DEAD(name) && !NAME_FETCH(name)) {
-               unlink_name(adb, name);
+               result = unlink_name(adb, name);
                free_adbname(adb, &name);
-               return;
+               if (result)
+                       result = dec_adb_irefcnt(adb);
+               return (result);
        }
 
        /*
@@ -700,29 +704,36 @@ kill_name(dns_adbname_t **n, isc_eventtype_t ev) {
         * in that they will always empty the list.
         */
        clean_finds_at_name(name, ev, DNS_ADBFIND_ADDRESSMASK);
-       clean_namehooks(adb, &name->v4);
-       clean_namehooks(adb, &name->v6);
+       result4 = clean_namehooks(adb, &name->v4);
+       result6 = clean_namehooks(adb, &name->v6);
        clean_target(adb, &name->target);
+       result = ISC_TF(result4 || result6);
 
        /*
         * If fetches are running, cancel them.  If none are running, we can
         * just kill the name here.
         */
        if (!NAME_FETCH(name)) {
-               unlink_name(adb, name);
+               INSIST(result == ISC_FALSE);
+               result = unlink_name(adb, name);
                free_adbname(adb, &name);
+               if (result)
+                       result = dec_adb_irefcnt(adb);
        } else {
                name->flags |= NAME_IS_DEAD;
                cancel_fetches_at_name(name);
        }
+       return (result);
 }
 
 /*
  * Requires the name's bucket be locked and no entry buckets be locked.
  */
-static void
+static isc_boolean_t
 check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
        dns_adb_t *adb;
+       isc_boolean_t result4 = ISC_FALSE;
+       isc_boolean_t result6 = ISC_FALSE;
 
        INSIST(DNS_ADBNAME_VALID(name));
        adb = name->adb;
@@ -734,7 +745,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
        if (!NAME_FETCH_V4(name) && EXPIRE_OK(name->expire_v4, now)) {
                if (NAME_HAS_V4(name)) {
                        DP(DEF_LEVEL, "expiring v4 for name %p", name);
-                       clean_namehooks(adb, &name->v4);
+                       result4 = clean_namehooks(adb, &name->v4);
                        name->partial_result &= ~DNS_ADBFIND_INET;
                }
                name->expire_v4 = INT_MAX;
@@ -746,7 +757,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
        if (!NAME_FETCH_V6(name) && EXPIRE_OK(name->expire_v6, now)) {
                if (NAME_HAS_V6(name)) {
                        DP(DEF_LEVEL, "expiring v6 for name %p", name);
-                       clean_namehooks(adb, &name->v6);
+                       result6 = clean_namehooks(adb, &name->v6);
                        name->partial_result &= ~DNS_ADBFIND_INET6;
                }
                name->expire_v6 = INT_MAX;
@@ -759,6 +770,7 @@ check_expire_namehooks(dns_adbname_t *name, isc_stdtime_t now) {
                clean_target(adb, &name->target);
                name->expire_target = INT_MAX;
        }
+       return (ISC_TF(result4 || result6));
 }
 
 /*
@@ -776,9 +788,10 @@ link_name(dns_adb_t *adb, int bucket, dns_adbname_t *name) {
 /*
  * Requires the name's bucket be locked.
  */
-static inline void
+static inline isc_boolean_t
 unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
        int bucket;
+       isc_boolean_t result = ISC_FALSE;
 
        bucket = name->lock_bucket;
        INSIST(bucket != DNS_ADB_INVALIDBUCKET);
@@ -788,7 +801,8 @@ unlink_name(dns_adb_t *adb, dns_adbname_t *name) {
        INSIST(adb->name_refcnt[bucket] > 0);
        adb->name_refcnt[bucket]--;
        if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
-               dec_adb_irefcnt(adb);
+               result = ISC_TRUE;
+       return (result);
 }
 
 /*
@@ -804,9 +818,10 @@ link_entry(dns_adb_t *adb, int bucket, dns_adbentry_t *entry) {
 /*
  * Requires the entry's bucket be locked.
  */
-static inline void
+static inline isc_boolean_t
 unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
        int bucket;
+       isc_boolean_t result = ISC_FALSE;
 
        bucket = entry->lock_bucket;
        INSIST(bucket != DNS_ADB_INVALIDBUCKET);
@@ -816,7 +831,8 @@ unlink_entry(dns_adb_t *adb, dns_adbentry_t *entry) {
        INSIST(adb->entry_refcnt[bucket] > 0);
        adb->entry_refcnt[bucket]--;
        if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
-               dec_adb_irefcnt(adb);
+               result = ISC_TRUE;
+       return (result);
 }
 
 static inline void
@@ -832,9 +848,10 @@ violate_locking_hierarchy(isc_mutex_t *have, isc_mutex_t *want) {
  * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
  * checked after calling this function.
  */
-static void
+static isc_boolean_t
 shutdown_names(dns_adb_t *adb) {
        int bucket;
+       isc_boolean_t result = ISC_FALSE;
        dns_adbname_t *name;
        dns_adbname_t *next_name;
 
@@ -849,7 +866,8 @@ shutdown_names(dns_adb_t *adb) {
                         * irefcnt ourselves, since it will not be
                         * automatically triggered by a name being unlinked.
                         */
-                       dec_adb_irefcnt(adb);
+                       INSIST(result == ISC_FALSE);
+                       result = dec_adb_irefcnt(adb);
                } else {
                        /*
                         * Run through the list.  For each name, clean up finds
@@ -859,22 +877,26 @@ shutdown_names(dns_adb_t *adb) {
                         */
                        while (name != NULL) {
                                next_name = ISC_LIST_NEXT(name, plink);
-                               kill_name(&name, DNS_EVENT_ADBSHUTDOWN);
+                               INSIST(result == ISC_FALSE);
+                               result = kill_name(&name,
+                                                  DNS_EVENT_ADBSHUTDOWN);
                                name = next_name;
                        }
                }
 
                UNLOCK(&adb->namelocks[bucket]);
        }
+       return (result);
 }
 
 /*
  * The ADB _MUST_ be locked before calling.  Also, exit conditions must be
  * checked after calling this function.
  */
-static void
+static isc_boolean_t
 shutdown_entries(dns_adb_t *adb) {
        int bucket;
+       isc_boolean_t result = ISC_FALSE;
        dns_adbentry_t *entry;
        dns_adbentry_t *next_entry;
 
@@ -889,7 +911,7 @@ shutdown_entries(dns_adb_t *adb) {
                         * irefcnt ourselves, since it will not be
                         * automatically triggered by an entry being unlinked.
                         */
-                       dec_adb_irefcnt(adb);
+                       result = dec_adb_irefcnt(adb);
                } else {
                        /*
                         * Run through the list.  Cleanup any entries not
@@ -899,8 +921,10 @@ shutdown_entries(dns_adb_t *adb) {
                                next_entry = ISC_LIST_NEXT(entry, plink);
                                if (entry->refcnt == 0 &&
                                    entry->expires != 0) {
-                                       unlink_entry(adb, entry);
+                                       result = unlink_entry(adb, entry);
                                        free_adbentry(adb, &entry);
+                                       if (result)
+                                               result = dec_adb_irefcnt(adb);
                                }
                                entry = next_entry;
                        }
@@ -908,6 +932,7 @@ shutdown_entries(dns_adb_t *adb) {
 
                UNLOCK(&adb->entrylocks[bucket]);
        }
+       return (result);
 }
 
 /*
@@ -935,11 +960,12 @@ cancel_fetches_at_name(dns_adbname_t *name) {
 /*
  * Assumes the name bucket is locked.
  */
-static void
+static isc_boolean_t
 clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
        dns_adbentry_t *entry;
        dns_adbnamehook_t *namehook;
        int addr_bucket;
+       isc_boolean_t result = ISC_FALSE;
 
        addr_bucket = DNS_ADB_INVALIDBUCKET;
        namehook = ISC_LIST_HEAD(*namehooks);
@@ -960,7 +986,7 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
                                LOCK(&adb->entrylocks[addr_bucket]);
                        }
 
-                       dec_entry_refcnt(adb, entry, ISC_FALSE);
+                       result = dec_entry_refcnt(adb, entry, ISC_FALSE);
                }
 
                /*
@@ -975,6 +1001,7 @@ clean_namehooks(dns_adb_t *adb, dns_adbnamehooklist_t *namehooks) {
 
        if (addr_bucket != DNS_ADB_INVALIDBUCKET)
                UNLOCK(&adb->entrylocks[addr_bucket]);
+       return (result);
 }
 
 static void
@@ -1163,23 +1190,33 @@ clean_finds_at_name(dns_adbname_t *name, isc_eventtype_t evtype,
 static inline void
 check_exit(dns_adb_t *adb) {
        isc_event_t *event;
-       isc_task_t *etask;
-       isc_boolean_t zeroirefcnt, zeroerefcnt;
-
        /*
         * The caller must be holding the adb lock.
         */
+       if (adb->shutting_down) {
+               /*
+                * If there aren't any external references either, we're
+                * done.  Send the control event to initiate shutdown.
+                */
+               INSIST(!adb->cevent_sent);      /* Sanity check. */
+               event = &adb->cevent;
+               isc_task_send(adb->task, &event);
+               adb->cevent_sent = ISC_TRUE;
+       }
+}
+
+static inline isc_boolean_t
+dec_adb_irefcnt(dns_adb_t *adb) {
+       isc_event_t *event;
+       isc_task_t *etask;
+       isc_boolean_t result = ISC_FALSE;
 
        LOCK(&adb->reflock);
-       zeroirefcnt = ISC_TF(adb->irefcnt == 0);
-       zeroerefcnt = ISC_TF(adb->erefcnt == 0);
-       UNLOCK(&adb->reflock);
 
-       if (adb->shutting_down && zeroirefcnt &&
-           isc_mempool_getallocated(adb->ahmp) == 0) {
-               /*
-                * We're now shutdown.  Send any whenshutdown events.
-                */
+       INSIST(adb->irefcnt > 0);
+       adb->irefcnt--;
+
+       if (adb->irefcnt == 0) {
                event = ISC_LIST_HEAD(adb->whenshutdown);
                while (event != NULL) {
                        ISC_LIST_UNLINK(adb->whenshutdown, event, ev_link);
@@ -1188,27 +1225,18 @@ check_exit(dns_adb_t *adb) {
                        isc_task_sendanddetach(&etask, &event);
                        event = ISC_LIST_HEAD(adb->whenshutdown);
                }
-
-               /*
-                * If there aren't any external references either, we're
-                * done.  Send the control event to initiate shutdown.
-                */
-               if (zeroerefcnt) {
-                       INSIST(!adb->cevent_sent);      /* Sanity check. */
-                       event = &adb->cevent;
-                       isc_task_send(adb->task, &event);
-                       adb->cevent_sent = ISC_TRUE;
-               }
        }
+
+       if (adb->irefcnt == 0 && adb->erefcnt == 0)
+               result = ISC_TRUE;
+       UNLOCK(&adb->reflock);
+       return (result);
 }
 
 static inline void
-dec_adb_irefcnt(dns_adb_t *adb) {
+inc_adb_irefcnt(dns_adb_t *adb) {
        LOCK(&adb->reflock);
-
-       INSIST(adb->irefcnt > 0);
-       adb->irefcnt--;
-
+       adb->irefcnt++;
        UNLOCK(&adb->reflock);
 }
 
@@ -1234,10 +1262,11 @@ inc_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
                UNLOCK(&adb->entrylocks[bucket]);
 }
 
-static inline void
+static inline isc_boolean_t
 dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
        int bucket;
        isc_boolean_t destroy_entry;
+       isc_boolean_t result = ISC_FALSE;
 
        bucket = entry->lock_bucket;
 
@@ -1251,18 +1280,22 @@ dec_entry_refcnt(dns_adb_t *adb, dns_adbentry_t *entry, isc_boolean_t lock) {
        if (entry->refcnt == 0 &&
            (adb->entry_sd[bucket] || entry->expires == 0)) {
                destroy_entry = ISC_TRUE;
-               unlink_entry(adb, entry);
+               result = unlink_entry(adb, entry);
        }
 
        if (lock)
                UNLOCK(&adb->entrylocks[bucket]);
 
        if (!destroy_entry)
-               return;
+               return (result);
 
        entry->lock_bucket = DNS_ADB_INVALIDBUCKET;
 
        free_adbentry(adb, &entry);
+       if (result)
+               result =dec_adb_irefcnt(adb);
+       
+       return (result);
 }
 
 static inline dns_adbname_t *
@@ -1477,6 +1510,7 @@ new_adbfind(dns_adb_t *adb) {
        ISC_EVENT_INIT(&h->event, sizeof (isc_event_t), 0, 0, 0, NULL, NULL,
                       NULL, NULL, h);
 
+       inc_adb_irefcnt(adb);
        h->magic = DNS_ADBFIND_MAGIC;
        return (h);
 }
@@ -1666,7 +1700,7 @@ free_adbfetch6(dns_adb_t *adb, dns_adbfetch6_t **fetch) {
        isc_mempool_put(adb->af6mp, f);
 }
 
-static inline void
+static inline isc_boolean_t
 free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
        dns_adbfind_t *find;
 
@@ -1684,6 +1718,7 @@ free_adbfind(dns_adb_t *adb, dns_adbfind_t **findp) {
 
        DESTROYLOCK(&find->lock);
        isc_mempool_put(adb->ahmp, find);
+       return (dec_adb_irefcnt(adb));
 }
 
 /*
@@ -1939,94 +1974,105 @@ shutdown_task(isc_task_t *task, isc_event_t *ev) {
 /*
  * name bucket must be locked; adb may be locked; no other locks held.
  */
-static void
+static isc_boolean_t
 check_expire_name(dns_adbname_t **namep, isc_stdtime_t now) {
        dns_adbname_t *name;
+       isc_result_t result = ISC_FALSE;
 
        INSIST(namep != NULL && DNS_ADBNAME_VALID(*namep));
        name = *namep;
 
        if (NAME_HAS_V4(name) || NAME_HAS_V6(name))
-               return;
+               return (result);
        if (NAME_FETCH(name))
-               return;
+               return (result);
        if (!EXPIRE_OK(name->expire_v4, now))
-               return;
+               return (result);
        if (!EXPIRE_OK(name->expire_v6, now))
-               return;
+               return (result);
        if (!EXPIRE_OK(name->expire_target, now))
-               return;
+               return (result);
 
        /*
         * The name is empty.  Delete it.
         */
-       kill_name(&name, DNS_EVENT_ADBEXPIRED);
+       result = kill_name(&name, DNS_EVENT_ADBEXPIRED);
        *namep = NULL;
 
        /*
         * Our caller, or one of its callers, will be calling check_exit() at
         * some point, so we don't need to do it here.
         */
+       return (result);
 }
 
 /*
  * entry bucket must be locked; adb may be locked; no other locks held.
  */
-static void
+static isc_boolean_t
 check_expire_entry(dns_adb_t *adb, dns_adbentry_t **entryp, isc_stdtime_t now)
 {
        dns_adbentry_t *entry;
+       isc_boolean_t result = ISC_FALSE;
 
        INSIST(entryp != NULL && DNS_ADBENTRY_VALID(*entryp));
        entry = *entryp;
 
        if (entry->refcnt != 0)
-               return;
+               return (result);
        if (entry->expires == 0 || entry->expires > now)
-               return;
+               return (result);
 
        /*
         * The entry is not in use.  Delete it.
         */
        DP(DEF_LEVEL, "killing entry %p", entry);
        INSIST(ISC_LINK_LINKED(entry, plink));
-       unlink_entry(adb, entry);
+       result = unlink_entry(adb, entry);
        free_adbentry(adb, &entry);
+       if (result)
+               dec_adb_irefcnt(adb);
        *entryp = NULL;
+       return (result);
 }
 
 /*
  * ADB must be locked, and no other locks held.
  */
-static void
+static isc_boolean_t
 cleanup_names(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
        dns_adbname_t *name;
        dns_adbname_t *next_name;
+       isc_result_t result = ISC_FALSE;
 
        DP(CLEAN_LEVEL, "cleaning name bucket %d", bucket);
 
        LOCK(&adb->namelocks[bucket]);
        if (adb->name_sd[bucket]) {
                UNLOCK(&adb->namelocks[bucket]);
-               return;
+               return (result);
        }
 
        name = ISC_LIST_HEAD(adb->names[bucket]);
        while (name != NULL) {
                next_name = ISC_LIST_NEXT(name, plink);
-               check_expire_namehooks(name, now);
-               check_expire_name(&name, now);
+               INSIST(result == ISC_FALSE);
+               result = check_expire_namehooks(name, now);
+               if (!result)
+                       result = check_expire_name(&name, now);
                name = next_name;
        }
        UNLOCK(&adb->namelocks[bucket]);
+       return (result);
 }
 
 /*
  * ADB must be locked, and no other locks held.
  */
-static void
+static isc_boolean_t
 cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
        dns_adbentry_t *entry, *next_entry;
+       isc_boolean_t result = ISC_FALSE;
 
        DP(CLEAN_LEVEL, "cleaning entry bucket %d", bucket);
 
@@ -2034,10 +2080,12 @@ cleanup_entries(dns_adb_t *adb, int bucket, isc_stdtime_t now) {
        entry = ISC_LIST_HEAD(adb->entries[bucket]);
        while (entry != NULL) {
                next_entry = ISC_LIST_NEXT(entry, plink);
-               check_expire_entry(adb, &entry, now);
+               INSIST(result == ISC_FALSE);
+               result = check_expire_entry(adb, &entry, now);
                entry = next_entry;
        }
        UNLOCK(&adb->entrylocks[bucket]);
+       return (result);
 }
 
 static void
@@ -2059,8 +2107,10 @@ timer_cleanup(isc_task_t *task, isc_event_t *ev) {
                /*
                 * Call our cleanup routines.
                 */
-               cleanup_names(adb, adb->next_cleanbucket, now);
-               cleanup_entries(adb, adb->next_cleanbucket, now);
+               INSIST(cleanup_names(adb, adb->next_cleanbucket, now) ==
+                        ISC_FALSE);
+               INSIST(cleanup_entries(adb, adb->next_cleanbucket, now) ==
+                        ISC_FALSE);
 
                /*
                 * Set the next bucket to be cleaned.
@@ -2310,7 +2360,7 @@ dns_adb_attach(dns_adb_t *adb, dns_adb_t **adbx) {
 void
 dns_adb_detach(dns_adb_t **adbx) {
        dns_adb_t *adb;
-       isc_boolean_t zeroerefcnt;
+       isc_boolean_t need_exit_check;
 
        REQUIRE(adbx != NULL && DNS_ADB_VALID(*adbx));
 
@@ -2321,13 +2371,13 @@ dns_adb_detach(dns_adb_t **adbx) {
 
        LOCK(&adb->reflock);
        adb->erefcnt--;
-       zeroerefcnt = ISC_TF(adb->erefcnt == 0);
+       need_exit_check = ISC_TF(adb->erefcnt == 0 && adb->irefcnt == 0);
        UNLOCK(&adb->reflock);
 
-       if (zeroerefcnt) {
+       if (need_exit_check) {
                LOCK(&adb->lock);
-               check_exit(adb);
                INSIST(adb->shutting_down);
+               check_exit(adb);
                UNLOCK(&adb->lock);
        }
 }
@@ -2352,7 +2402,6 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
 
        LOCK(&adb->reflock);
        zeroirefcnt = ISC_TF(adb->irefcnt == 0);
-       UNLOCK(&adb->reflock);
 
        if (adb->shutting_down && zeroirefcnt &&
            isc_mempool_getallocated(adb->ahmp) == 0) {
@@ -2368,11 +2417,13 @@ dns_adb_whenshutdown(dns_adb_t *adb, isc_task_t *task, isc_event_t **eventp) {
                ISC_LIST_APPEND(adb->whenshutdown, event, ev_link);
        }
 
+       UNLOCK(&adb->reflock);
        UNLOCK(&adb->lock);
 }
 
 void
 dns_adb_shutdown(dns_adb_t *adb) {
+       isc_boolean_t need_check_exit;
 
        /*
         * Shutdown 'adb'.
@@ -2382,9 +2433,11 @@ dns_adb_shutdown(dns_adb_t *adb) {
 
        if (!adb->shutting_down) {
                adb->shutting_down = ISC_TRUE;
-               shutdown_names(adb);
-               shutdown_entries(adb);
-               check_exit(adb);
+               need_check_exit = shutdown_names(adb);
+               if (!need_check_exit)
+                       need_check_exit = shutdown_entries(adb);
+               if (need_check_exit)
+                       check_exit(adb);
        }
 
        UNLOCK(&adb->lock);
@@ -2471,7 +2524,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
        if (adb->name_sd[bucket]) {
                DP(DEF_LEVEL,
                   "dns_adb_createfind: returning ISC_R_SHUTTINGDOWN");
-               free_adbfind(adb, &find);
+               INSIST(free_adbfind(adb, &find) == ISC_FALSE);
                result = ISC_R_SHUTTINGDOWN;
                goto out;
        }
@@ -2482,7 +2535,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
        if (adbname == NULL) {
                adbname = new_adbname(adb, name);
                if (adbname == NULL) {
-                       free_adbfind(adb, &find);
+                       INSIST(free_adbfind(adb, &find) == ISC_FALSE);
                        result = ISC_R_NOMEMORY;
                        goto out;
                }
@@ -2498,7 +2551,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
        /*
         * Expire old entries, etc.
         */
-       check_expire_namehooks(adbname, now);
+       INSIST(check_expire_namehooks(adbname, now) == ISC_FALSE);
 
        /*
         * Do we know that the name is an alias?
@@ -2781,7 +2834,7 @@ dns_adb_destroyfind(dns_adbfind_t **findp) {
                entry = ai->entry;
                ai->entry = NULL;
                INSIST(DNS_ADBENTRY_VALID(entry));
-               dec_entry_refcnt(adb, entry, ISC_TRUE);
+               INSIST(dec_entry_refcnt(adb, entry, ISC_TRUE) == ISC_FALSE);
                free_adbaddrinfo(adb, &ai);
                ai = ISC_LIST_HEAD(find->list);
        }
@@ -2794,8 +2847,8 @@ dns_adb_destroyfind(dns_adbfind_t **findp) {
         * lock is destroyed.
         */
        LOCK(&adb->lock);
-       free_adbfind(adb, &find);
-       check_exit(adb);
+       if (free_adbfind(adb, &find))
+               check_exit(adb);
        UNLOCK(&adb->lock);
 }
 
@@ -3381,14 +3434,7 @@ fetch_callback(isc_task_t *task, isc_event_t *ev) {
                free_adbfetch(adb, &fetch);
                isc_event_free(&ev);
 
-               kill_name(&name, DNS_EVENT_ADBCANCELED);
-
-               /*
-                * If we're shutting down and this bucket is empty, we need
-                * to call check_exit() to see if we're done.
-                */
-               if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
-                       want_check_exit = ISC_TRUE;
+               want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
 
                UNLOCK(&adb->namelocks[bucket]);
 
@@ -3545,14 +3591,7 @@ fetch_callback_a6(isc_task_t *task, isc_event_t *ev) {
                free_adbfetch6(adb, &fetch);
                isc_event_free(&ev);
 
-               kill_name(&name, DNS_EVENT_ADBCANCELED);
-
-               /*
-                * If we're shutting down and this bucket is empty, we need
-                * to call check_exit() to see if we're done.
-                */
-               if (adb->name_sd[bucket] && adb->name_refcnt[bucket] == 0)
-                       want_check_exit = ISC_TRUE;
+               want_check_exit = kill_name(&name, DNS_EVENT_ADBCANCELED);
 
                UNLOCK(&adb->namelocks[bucket]);
 
@@ -4007,9 +4046,7 @@ dns_adb_freeaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **addrp) {
 
        entry->expires = now + 1800;            /* XXXRTH */
 
-       dec_entry_refcnt(adb, entry, ISC_FALSE);
-       if (adb->entry_sd[bucket] && adb->entry_refcnt[bucket] == 0)
-               want_check_exit = ISC_TRUE;
+       want_check_exit = dec_entry_refcnt(adb, entry, ISC_FALSE);
 
        UNLOCK(&adb->entrylocks[bucket]);
 
@@ -4035,8 +4072,8 @@ dns_adb_flush(dns_adb_t *adb) {
                /*
                 * Call our cleanup routines.
                 */
-               cleanup_names(adb, i, INT_MAX);
-               cleanup_entries(adb, i, INT_MAX);
+               INSIST(cleanup_names(adb, i, INT_MAX) == ISC_FALSE);
+               INSIST(cleanup_entries(adb, i, INT_MAX) == ISC_FALSE);
        }
 
 #ifdef DUMP_ADB_AFTER_CLEANING