1813. [func] Restructured the data locking framework using

			architecture dependent atomic operations (when
			available), improving response performance on
			multi-processor machines significantly.
			x86, x86_64, alpha, and sparc64 are currently
			supported.

(RT #13505)
parent ef67e6d8
......@@ -146,7 +146,12 @@
1814. [func] UNIX domain controls are now supported.
1813. [placeholder] rt13505
1813. [func] Restructured the data locking framework using
architecture dependent atomic operations (when
available), improving response performance on
multi-processor machines significantly.
x86, x86_64, alpha, and sparc64 are currently
supported.
1812. [port] win32: IN6_IS_ADDR_UNSPECIFIED macro is incorrect.
[RT #13453]
......
......@@ -15,13 +15,14 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: client.c,v 1.226 2005/04/27 04:55:48 sra Exp $ */
/* $Id: client.c,v 1.227 2005/06/04 05:32:46 jinmei Exp $ */
#include <config.h>
#include <isc/formatcheck.h>
#include <isc/mutex.h>
#include <isc/once.h>
#include <isc/platform.h>
#include <isc/print.h>
#include <isc/stdio.h>
#include <isc/string.h>
......@@ -471,7 +472,7 @@ exit_check(ns_client_t *client) {
CTRACE("free");
client->magic = 0;
isc_mem_put(client->mctx, client, sizeof(*client));
isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
goto unlock;
}
......@@ -1667,6 +1668,7 @@ static isc_result_t
client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
ns_client_t *client;
isc_result_t result;
isc_mem_t *mctx = NULL;
/*
* Caller must be holding the manager lock.
......@@ -1678,9 +1680,31 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
REQUIRE(clientp != NULL && *clientp == NULL);
client = isc_mem_get(manager->mctx, sizeof(*client));
if (client == NULL)
#ifdef ISC_PLATFORM_USETHREADS
/*
* When enabling threads, we use a separate memory context for each
* client, since concurrent access to a shared context would cause
* heavy contentions. We also specify the NOLOCK flag on creation,
* since we are very sure that multiple threads will never get access
* to the context simultaneously.
*/
result = isc_mem_create2(0, 0, &mctx, ISC_MEMFLAG_NOLOCK);
if (result != ISC_R_SUCCESS)
return (result);
#else
/*
* Otherwise, simply share manager's context. Using a separate context
* in this case would simply waste memory.
*/
isc_mem_attach(manager->mctx, &mctx);
#endif
client = isc_mem_get(mctx, sizeof(*client));
if (client == NULL) {
isc_mem_detach(&mctx);
return (ISC_R_NOMEMORY);
}
client->mctx = mctx;
client->task = NULL;
result = isc_task_create(manager->taskmgr, 0, &client->task);
......@@ -1697,7 +1721,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
client->timerset = ISC_FALSE;
client->message = NULL;
result = dns_message_create(manager->mctx, DNS_MESSAGE_INTENTPARSE,
result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
&client->message);
if (result != ISC_R_SUCCESS)
goto cleanup_timer;
......@@ -1705,7 +1729,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
/* XXXRTH Hardwired constants */
client->sendevent = (isc_socketevent_t *)
isc_event_allocate(manager->mctx, client,
isc_event_allocate(client->mctx, client,
ISC_SOCKEVENT_SENDDONE,
client_senddone, client,
sizeof(isc_socketevent_t));
......@@ -1714,14 +1738,14 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
goto cleanup_message;
}
client->recvbuf = isc_mem_get(manager->mctx, RECV_BUFFER_SIZE);
client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
if (client->recvbuf == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup_sendevent;
}
client->recvevent = (isc_socketevent_t *)
isc_event_allocate(manager->mctx, client,
isc_event_allocate(client->mctx, client,
ISC_SOCKEVENT_RECVDONE,
client_request, client,
sizeof(isc_socketevent_t));
......@@ -1731,7 +1755,6 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
}
client->magic = NS_CLIENT_MAGIC;
client->mctx = manager->mctx;
client->manager = NULL;
client->state = NS_CLIENTSTATE_INACTIVE;
client->newstate = NS_CLIENTSTATE_MAX;
......@@ -1801,7 +1824,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
isc_event_free((isc_event_t **)&client->recvevent);
cleanup_recvbuf:
isc_mem_put(manager->mctx, client->recvbuf, RECV_BUFFER_SIZE);
isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
cleanup_sendevent:
isc_event_free((isc_event_t **)&client->sendevent);
......@@ -1818,7 +1841,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
isc_task_detach(&client->task);
cleanup_client:
isc_mem_put(manager->mctx, client, sizeof(*client));
isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
return (result);
}
......
......@@ -18,7 +18,7 @@ AC_DIVERT_PUSH(1)dnl
esyscmd([sed "s/^/# /" COPYRIGHT])dnl
AC_DIVERT_POP()dnl
AC_REVISION($Revision: 1.376 $)
AC_REVISION($Revision: 1.377 $)
AC_INIT(lib/dns/name.c)
AC_PREREQ(2.13)
......@@ -1830,6 +1830,76 @@ yes)
esac
AC_SUBST(ISC_PLATFORM_HAVEIFNAMETOINDEX)
#
# Machine architecture dependent features
#
AC_ARG_ENABLE(atomic,
[ --enable-atomic enable machine specific atomic operations
[[default=autodetect]]],
enable_atomic="$enableval",
enable_atomic="autodetect")
case "$enable_atomic" in
yes|''|autodetect)
use_atomic=yes
;;
no)
use_atomic=no
;;
esac
ISC_PLATFORM_USEOSFASM="#undef ISC_PLATFORM_USEOSFASM"
if test "$use_atomic" = "yes"; then
AC_MSG_CHECKING([architecture type for atomic operations])
case "$host" in
[i[3456]86-*]|x86_64-*)
# XXX: also need to check portability of the "asm" keyword?
# XXX: some old x86 architectures actualy do not support
# (some of) these operations. Do we need stricter checks?
# Note: We currently use the same code for both the x86_32 and
# x86_64 architectures, but there may be a better
# implementation for the latter.
have_atomic=yes
arch=x86_32
;;
alpha*-*)
have_atomic=yes
arch=alpha
if test "X$GCC" != "Xyes"; then
case "$host" in
*-dec-osf*)
# Tru64 compiler has its own syntax for inline
# assembly.
ISC_PLATFORM_USEOSFASM="#define ISC_PLATFORM_USEOSFASM 1"
;;
esac
fi
;;
*)
have_atomic=no
arch=noatomic
;;
esac
AC_MSG_RESULT($arch)
fi
if test "$have_atomic" = "yes"; then
ISC_PLATFORM_HAVEXADD="#define ISC_PLATFORM_HAVEXADD 1"
ISC_PLATFORM_HAVECMPXCHG="#define ISC_PLATFORM_HAVECMPXCHG 1"
ISC_PLATFORM_HAVEATOMICSTORE="#define ISC_PLATFORM_HAVEATOMICSTORE 1"
else
ISC_PLATFORM_HAVEXADD="#undef ISC_PLATFORM_HAVEXADD"
ISC_PLATFORM_HAVECMPXCHG="#undef ISC_PLATFORM_HAVECMPXCHG"
ISC_PLATFORM_HAVEATOMICSTORE="#undef ISC_PLATFORM_HAVEATOMICSTORE"
fi
AC_SUBST(ISC_PLATFORM_HAVEXADD)
AC_SUBST(ISC_PLATFORM_HAVECMPXCHG)
AC_SUBST(ISC_PLATFORM_HAVEATOMICSTORE)
AC_SUBST(ISC_PLATFORM_USEOSFASM)
ISC_ARCH_DIR=$arch
AC_SUBST(ISC_ARCH_DIR)
#
# The following sections deal with tools used for formatting
# the documentation. They are all optional, unless you are
......
......@@ -14,10 +14,11 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: acache.c,v 1.8 2005/02/07 00:04:05 marka Exp $ */
/* $Id: acache.c,v 1.9 2005/06/04 05:32:46 jinmei Exp $ */
#include <config.h>
#include <isc/atomic.h>
#include <isc/event.h>
#include <isc/hash.h>
#include <isc/magic.h>
......@@ -25,6 +26,7 @@
#include <isc/mutex.h>
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/task.h>
#include <isc/time.h>
#include <isc/timer.h>
......@@ -73,6 +75,26 @@
#define DNS_ACACHE_MINSIZE 2097152 /* Bytes. 2097152 = 2 MB */
#define DNS_ACACHE_CLEANERINCREMENT 1000 /* Number of entries. */
#if defined(ISC_RWLOCK_USEATOMIC) && defined(ISC_PLATFORM_HAVEATOMICSTORE)
#define ACACHE_USE_RWLOCK 1
#endif
#ifdef ACACHE_USE_RWLOCK
#define ACACHE_INITLOCK(l) isc_rwlock_init((l), 0, 0)
#define ACACHE_DESTROYLOCK(l) isc_rwlock_destroy(l)
#define ACACHE_LOCK(l, t) RWLOCK((l), (t))
#define ACACHE_UNLOCK(l, t) RWUNLOCK((l), (t))
#define acache_storetime(entry, t) (isc_atomic_store(&(entry)->lastused, (t)))
#else
#define ACACHE_INITLOCK(l) isc_mutex_init(l)
#define ACACHE_DESTROYLOCK(l) DESTROYLOCK(l)
#define ACACHE_LOCK(l, t) LOCK(l)
#define ACACHE_UNLOCK(l, t) UNLOCK(l)
#define acache_storetime(entry, t) ((entry)->lastused = (t))
#endif
/* Locked by acache lock */
typedef struct dbentry {
ISC_LINK(struct dbentry) link;
......@@ -157,7 +179,11 @@ struct dns_acache {
struct dns_acacheentry {
unsigned int magic;
#ifdef ACACHE_USE_RWLOCK
isc_rwlock_t lock;
#else
isc_mutex_t lock;
#endif
isc_refcount_t references;
dns_acache_t *acache;
......@@ -185,7 +211,7 @@ struct dns_acacheentry {
void *cbarg;
/* Timestamp of the last time this entry is referred to */
isc_stdtime_t lastused;
isc_stdtime32_t lastused;
};
/*
......@@ -242,7 +268,7 @@ shutdown_entries(dns_acache_t *acache) {
entry = entry_next) {
entry_next = ISC_LIST_NEXT(entry, link);
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
/*
* If the cleaner holds this entry, it will be unlinked and
......@@ -256,7 +282,7 @@ shutdown_entries(dns_acache_t *acache) {
entry->callback = NULL;
}
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
if (acache->cleaner.current_entry != entry)
dns_acache_detachentry(&entry);
......@@ -352,7 +378,7 @@ destroy_entry(dns_acacheentry_t *entry) {
*/
clear_entry(acache, entry);
DESTROYLOCK(&entry->lock);
ACACHE_DESTROYLOCK(&entry->lock);
isc_mem_put(acache->mctx, entry, sizeof(*entry));
......@@ -666,6 +692,7 @@ entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
isc_stdtime_t now)
{
unsigned int interval = cleaner->cleaning_interval;
isc_stdtime32_t now32;
/*
* If the callback has been canceled, we definitely do not need the
......@@ -674,7 +701,8 @@ entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
if (entry->callback == NULL)
return (ISC_TRUE);
if (entry->lastused + interval < now)
isc_stdtime_convert32(now, &now32);
if (entry->lastused + interval < now32)
return (ISC_TRUE);
/*
......@@ -683,7 +711,8 @@ entry_stale(acache_cleaner_t *cleaner, dns_acacheentry_t *entry,
* use and the cleaning interval.
*/
if (cleaner->overmem) {
unsigned int passed = now - entry->lastused; /* <= interval */
unsigned int passed =
now32 - entry->lastused; /* <= interval */
isc_uint32_t val, r;
isc_random_get(&val);
......@@ -734,7 +763,7 @@ acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
next = ISC_LIST_NEXT(entry, link);
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
is_stale = entry_stale(cleaner, entry, now);
if (is_stale) {
......@@ -747,7 +776,7 @@ acache_incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
cleaner->ncleaned++;
}
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
if (is_stale)
dns_acache_detachentry(&entry);
......@@ -1129,7 +1158,7 @@ dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
* original holder has canceled callback,) destroy it here.
*/
while ((entry = ISC_LIST_HEAD(dbentry->originlist)) != NULL) {
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
/*
* Releasing olink first would avoid finddbent() in
......@@ -1144,13 +1173,13 @@ dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
(entry->callback)(entry, &entry->cbarg);
entry->callback = NULL;
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
if (acache->cleaner.current_entry != entry)
dns_acache_detachentry(&entry);
}
while ((entry = ISC_LIST_HEAD(dbentry->referlist)) != NULL) {
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
ISC_LIST_UNLINK(dbentry->referlist, entry, rlink);
if (acache->cleaner.current_entry != entry)
......@@ -1161,7 +1190,7 @@ dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
(entry->callback)(entry, &entry->cbarg);
entry->callback = NULL;
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
if (acache->cleaner.current_entry != entry)
dns_acache_detachentry(&entry);
......@@ -1200,7 +1229,7 @@ dns_acache_createentry(dns_acache_t *acache, dns_db_t *origdb,
if (newentry == NULL)
return (ISC_R_NOMEMORY);
result = isc_mutex_init(&newentry->lock);
result = ACACHE_INITLOCK(&newentry->lock);
if (result != ISC_R_SUCCESS) {
isc_mem_put(acache->mctx, newentry, sizeof(*newentry));
UNEXPECTED_ERROR(__FILE__, __LINE__,
......@@ -1246,6 +1275,7 @@ dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
{
isc_result_t result = ISC_R_SUCCESS;
dns_rdataset_t *erdataset;
isc_stdtime32_t now32;
REQUIRE(DNS_ACACHEENTRY_VALID(entry));
REQUIRE(zonep == NULL || *zonep == NULL);
......@@ -1254,10 +1284,11 @@ dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
REQUIRE(nodep != NULL && *nodep == NULL);
REQUIRE(fname != NULL);
REQUIRE(msg != NULL);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_read);
LOCK(&entry->lock);
entry->lastused = now;
isc_stdtime_convert32(now, &now32);
acache_storetime(entry, now32);
if (entry->zone != NULL && zonep != NULL)
dns_zone_attach(entry->zone, zonep);
......@@ -1284,7 +1315,8 @@ dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
ardataset = NULL;
result = dns_message_gettemprdataset(msg, &ardataset);
if (result != ISC_R_SUCCESS) {
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock,
isc_rwlocktype_read);
goto fail;
}
......@@ -1300,7 +1332,7 @@ dns_acache_getentry(dns_acacheentry_t *entry, dns_zone_t **zonep,
}
}
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_read);
return (result);
......@@ -1337,7 +1369,7 @@ dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
REQUIRE(DNS_ACACHEENTRY_VALID(entry));
LOCK(&acache->lock); /* XXX: need to lock it here for ordering */
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
/* Set zone */
if (zone != NULL)
......@@ -1429,7 +1461,7 @@ dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
*/
dns_acache_attachentry(entry, &dummy_entry);
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
UNLOCK(&acache->lock);
return (ISC_R_SUCCESS);
......@@ -1437,7 +1469,7 @@ dns_acache_setentry(dns_acache_t *acache, dns_acacheentry_t *entry,
fail:
clear_entry(acache, entry);
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
UNLOCK(&acache->lock);
return (result);
......@@ -1451,7 +1483,7 @@ dns_acache_cancelentry(dns_acacheentry_t *entry) {
INSIST(DNS_ACACHE_VALID(acache));
LOCK(&acache->lock);
LOCK(&entry->lock);
ACACHE_LOCK(&entry->lock, isc_rwlocktype_write);
/*
* Release dependencies stored in this entry as much as possible.
......@@ -1465,7 +1497,7 @@ dns_acache_cancelentry(dns_acacheentry_t *entry) {
entry->callback = NULL;
entry->cbarg = NULL;
UNLOCK(&entry->lock);
ACACHE_UNLOCK(&entry->lock, isc_rwlocktype_write);
UNLOCK(&acache->lock);
}
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rbt.h,v 1.62 2005/04/29 00:23:00 marka Exp $ */
/* $Id: rbt.h,v 1.63 2005/06/04 05:32:47 jinmei Exp $ */
#ifndef DNS_RBT_H
#define DNS_RBT_H 1
......@@ -24,6 +24,7 @@
#include <isc/lang.h>
#include <isc/magic.h>
#include <isc/refcount.h>
#include <dns/types.h>
......@@ -42,6 +43,12 @@ ISC_LANG_BEGINDECLS
#define DNS_RBTFIND_NOPREDECESSOR 0x04
/*@}*/
#ifndef DNS_RBT_USEISCREFCOUNT
#ifdef ISC_REFCOUNT_HAVEATOMIC
#define DNS_RBT_USEISCREFCOUNT 1
#endif
#endif
/*
* These should add up to 30.
*/
......@@ -108,7 +115,11 @@ typedef struct dns_rbtnode {
unsigned int dirty:1;
unsigned int wild:1;
unsigned int locknum:DNS_RBT_LOCKLENGTH;
#ifndef DNS_RBT_USEISCREFCOUNT
unsigned int references:DNS_RBT_REFLENGTH;
#else
isc_refcount_t references; /* note that this is not in the bitfield */
#endif
/*@}*/
} dns_rbtnode_t;
......@@ -211,7 +222,10 @@ typedef struct dns_rbtnodechain {
/*****
***** Public interfaces.
*****/
isc_result_t
dns_rbt_create2(isc_mem_t *mctx, void (*deleter)(void *, void *),
void (*deleter2)(void *, void *),
void *deleter_arg, dns_rbt_t **rbtp);
isc_result_t
dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
void *deleter_arg, dns_rbt_t **rbtp);
......@@ -844,6 +858,63 @@ dns_rbtnodechain_next(dns_rbtnodechain_t *chain, dns_name_t *name,
*\li &lt;something_else> Any error result from dns_name_concatenate.
*/
/*
* Wrapper macros for manipulating the rbtnode reference counter:
* Since we selectively use isc_refcount_t for the reference counter of
* a rbtnode, operations on the counter depend on the actual type of it.
* The following macros provide a common interface to these operations,
* hiding the back-end. The usage is the same as that of isc_refcount_xxx().
*/
#ifdef DNS_RBT_USEISCREFCOUNT
#define dns_rbtnode_refinit(node, n) \
do { \
isc_refcount_init(&(node)->references, (n)); \
} while (0)
#define dns_rbtnode_refdestroy(node) \
do { \
isc_refcount_destroy(&(node)->references); \
} while (0)
#define dns_rbtnode_refcurrent(node) \
isc_refcount_current(&(node)->references)
#define dns_rbtnode_refincrement0(node, refs) \
do { \
isc_refcount_increment0(&(node)->references, (refs)); \
} while (0)
#define dns_rbtnode_refincrement(node, refs) \
do { \
isc_refcount_increment(&(node)->references, (refs)); \
} while (0)
#define dns_rbtnode_refdecrement(node, refs) \
do { \
isc_refcount_decrement(&(node)->references, (refs)); \
} while (0)
#else /* DNS_RBT_USEISCREFCOUNT */
#define dns_rbtnode_refinit(node, n) ((node)->references = (n))
#define dns_rbtnode_refdestroy(node) (REQUIRE((node)->references == 0))
#define dns_rbtnode_refcurrent(node) ((node)->references)
#define dns_rbtnode_refincrement0(node, refs) \
do { \
unsigned int *_tmp = (unsigned int *)(refs); \
(node)->references++; \
if ((_tmp) != NULL) \
(*_tmp) = (node)->references; \
} while (0)
#define dns_rbtnode_refincrement(node, refs) \
do { \
REQUIRE((node)->references > 0); \
(node)->references++; \
if ((refs) != NULL) \
(*refs) = (node)->references; \
} while (0)
#define dns_rbtnode_refdecrement(node, refs) \
do { \
REQUIRE((node)->references > 0); \
(node)->references--; \
if ((refs) != NULL) \
(*refs) = (node)->references; \
} while (0)
#endif /* DNS_RBT_USEISCREFCOUNT */
ISC_LANG_ENDDECLS
#endif /* DNS_RBT_H */
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: rbt.c,v 1.132 2005/04/29 00:22:50 marka Exp $ */
/* $Id: rbt.c,v 1.133 2005/06/04 05:32:46 jinmei Exp $ */
/*! \file */
......@@ -26,6 +26,7 @@
#include <isc/mem.h>
#include <isc/platform.h>
#include <isc/print.h>
#include <isc/refcount.h>
#include <isc/string.h>
#include <isc/util.h>
......@@ -62,6 +63,7 @@ struct dns_rbt {
isc_mem_t * mctx;
dns_rbtnode_t * root;
void (*data_deleter)(void *, void *);
void (*data_deleter2)(void *, void *);
void * deleter_arg;
unsigned int nodecount;
unsigned int hashsize;
......@@ -96,7 +98,6 @@ struct dns_rbt {
#define DIRTY(node) ((node)->dirty)
#define WILD(node) ((node)->wild)
#define LOCKNUM(node) ((node)->locknum)
#define REFS(node) ((node)->references)
/*%
* The variable length stuff stored after the node.
......@@ -220,8 +221,9 @@ dns_rbt_deletetreeflat(dns_rbt_t *rbt, unsigned int quantum,
* Initialize a red/black tree of trees.
*/
isc_result_t
dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
void *deleter_arg, dns_rbt_t **rbtp)
dns_rbt_create2(isc_mem_t *mctx, void (*deleter)(void *, void *),
void (*deleter2)(void *, void *),
void *deleter_arg, dns_rbt_t **rbtp)
{
#ifdef DNS_RBT_USEHASH
isc_result_t result;
......@@ -231,7 +233,7 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
REQUIRE(mctx != NULL);
REQUIRE(rbtp != NULL && *rbtp == NULL);
REQUIRE(deleter == NULL ? deleter_arg == NULL : 1);
REQUIRE((deleter == NULL && deleter2 == NULL) ? deleter_arg == NULL : 1);
rbt = (dns_rbt_t *)isc_mem_get(mctx, sizeof(*rbt));
if (rbt == NULL)
......@@ -239,6 +241,7 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
rbt->mctx = mctx;
rbt->data_deleter = deleter;
rbt->data_deleter2 = deleter2;
rbt->deleter_arg = deleter_arg;
rbt->root = NULL;
rbt->nodecount = 0;
......@@ -258,6 +261,13 @@ dns_rbt_create(isc_mem_t *mctx, void (*deleter)(void *, void *),
return (ISC_R_SUCCESS);
}
isc_result_t