From ba8f85b49c38af7bc2a9acdef5dcde2de008d25e Mon Sep 17 00:00:00 2001
From: peter <peter@FreeBSD.org>
Date: Sat, 12 Jul 2008 05:00:28 +0000
Subject: Flatten bind9 vendor work area

---
 lib/isc/mem.c | 1955 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1955 insertions(+)
 create mode 100644 lib/isc/mem.c

(limited to 'lib/isc/mem.c')

diff --git a/lib/isc/mem.c b/lib/isc/mem.c
new file mode 100644
index 0000000..aec6118
--- /dev/null
+++ b/lib/isc/mem.c
@@ -0,0 +1,1955 @@
+/*
+ * Copyright (C) 2004-2007  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2003  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: mem.c,v 1.116.18.18 2007/10/30 23:31:43 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include <limits.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/msgs.h>
+#include <isc/once.h>
+#include <isc/ondestroy.h>
+#include <isc/string.h>
+
+#include <isc/mutex.h>
+#include <isc/util.h>
+
+#define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l)
+#define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l)
+
+#ifndef ISC_MEM_DEBUGGING
+#define ISC_MEM_DEBUGGING 0
+#endif
+LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING;
+
+/*
+ * Constants.
+ */
+
+#define DEF_MAX_SIZE		1100
+#define DEF_MEM_TARGET		4096
+#define ALIGNMENT_SIZE		8		/*%< must be a power of 2 */
+#define NUM_BASIC_BLOCKS	64		/*%< must be > 1 */
+#define TABLE_INCREMENT		1024
+#define DEBUGLIST_COUNT		1024
+
+/*
+ * Types.
+ */
+#if ISC_MEM_TRACKLINES
+typedef struct debuglink debuglink_t;
+struct debuglink {
+	ISC_LINK(debuglink_t)	link;
+	const void	       *ptr[DEBUGLIST_COUNT];
+	unsigned int		size[DEBUGLIST_COUNT];
+	const char	       *file[DEBUGLIST_COUNT];
+	unsigned int		line[DEBUGLIST_COUNT];
+	unsigned int		count;
+};
+
+#define FLARG_PASS	, file, line
+#define FLARG		, const char *file, int line
+#else
+#define FLARG_PASS
+#define FLARG
+#endif
+
+typedef struct element element;
+struct element {
+	element *		next;
+};
+
+typedef struct {
+	/*!
+	 * This structure must be ALIGNMENT_SIZE bytes.
+	 */
+	union {
+		size_t		size;
+		isc_mem_t	*ctx;
+		char		bytes[ALIGNMENT_SIZE];
+	} u;
+} size_info;
+
+struct stats {
+	unsigned long		gets;
+	unsigned long		totalgets;
+	unsigned long		blocks;
+	unsigned long		freefrags;
+};
+
+#define MEM_MAGIC		ISC_MAGIC('M', 'e', 'm', 'C')
+#define VALID_CONTEXT(c)	ISC_MAGIC_VALID(c, MEM_MAGIC)
+
+#if ISC_MEM_TRACKLINES
+typedef ISC_LIST(debuglink_t)	debuglist_t;
+#endif
+
+/* List of all active memory contexts. */
+
+static ISC_LIST(isc_mem_t)	contexts;
+static isc_once_t		once = ISC_ONCE_INIT;
+static isc_mutex_t		lock;
+
+struct isc_mem {
+	unsigned int		magic;
+	isc_ondestroy_t		ondestroy;
+	unsigned int		flags;
+	isc_mutex_t		lock;
+	isc_memalloc_t		memalloc;
+	isc_memfree_t		memfree;
+	void *			arg;
+	size_t			max_size;
+	isc_boolean_t		checkfree;
+	struct stats *		stats;
+	unsigned int		references;
+	size_t			quota;
+	size_t			total;
+	size_t			inuse;
+	size_t			maxinuse;
+	size_t			hi_water;
+	size_t			lo_water;
+	isc_boolean_t		hi_called;
+	isc_mem_water_t		water;
+	void *			water_arg;
+	ISC_LIST(isc_mempool_t)	pools;
+
+	/*  ISC_MEMFLAG_INTERNAL */
+	size_t			mem_target;
+	element **		freelists;
+	element *		basic_blocks;
+	unsigned char **	basic_table;
+	unsigned int		basic_table_count;
+	unsigned int		basic_table_size;
+	unsigned char *		lowest;
+	unsigned char *		highest;
+
+#if ISC_MEM_TRACKLINES
+	debuglist_t *	 	debuglist;
+#endif
+
+	unsigned int		memalloc_failures;
+	ISC_LINK(isc_mem_t)	link;
+};
+
+#define MEMPOOL_MAGIC		ISC_MAGIC('M', 'E', 'M', 'p')
+#define VALID_MEMPOOL(c)	ISC_MAGIC_VALID(c, MEMPOOL_MAGIC)
+
+struct isc_mempool {
+	/* always unlocked */
+	unsigned int	magic;		/*%< magic number */
+	isc_mutex_t    *lock;		/*%< optional lock */
+	isc_mem_t      *mctx;		/*%< our memory context */
+	/*%< locked via the memory context's lock */
+	ISC_LINK(isc_mempool_t)	link;	/*%< next pool in this mem context */
+	/*%< optionally locked from here down */
+	element	       *items;		/*%< low water item list */
+	size_t		size;		/*%< size of each item on this pool */
+	unsigned int	maxalloc;	/*%< max number of items allowed */
+	unsigned int	allocated;	/*%< # of items currently given out */
+	unsigned int	freecount;	/*%< # of items on reserved list */
+	unsigned int	freemax;	/*%< # of items allowed on free list */
+	unsigned int	fillcount;	/*%< # of items to fetch on each fill */
+	/*%< Stats only. */
+	unsigned int	gets;		/*%< # of requests to this pool */
+	/*%< Debugging only. */
+#if ISC_MEMPOOL_NAMES
+	char		name[16];	/*%< printed name in stats reports */
+#endif
+};
+
+/*
+ * Private Inline-able.
+ */
+
+#if ! ISC_MEM_TRACKLINES
+#define ADD_TRACE(a, b, c, d, e)
+#define DELETE_TRACE(a, b, c, d, e)
+#else
+#define ADD_TRACE(a, b, c, d, e) \
+	do { \
+		if ((isc_mem_debugging & (ISC_MEM_DEBUGTRACE | \
+					  ISC_MEM_DEBUGRECORD)) != 0 && \
+		     b != NULL) \
+		         add_trace_entry(a, b, c, d, e); \
+	} while (0)
+#define DELETE_TRACE(a, b, c, d, e)	delete_trace_entry(a, b, c, d, e)
+
+static void
+print_active(isc_mem_t *ctx, FILE *out);
+
+/*!
+ * mctx must be locked.
+ */
+static inline void
+add_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size
+		FLARG)
+{
+	debuglink_t *dl;
+	unsigned int i;
+
+	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
+		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+					       ISC_MSG_ADDTRACE,
+					       "add %p size %u "
+					       "file %s line %u mctx %p\n"),
+			ptr, size, file, line, mctx);
+
+	if (mctx->debuglist == NULL)
+		return;
+
+	if (size > mctx->max_size)
+		size = mctx->max_size;
+
+	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
+	while (dl != NULL) {
+		if (dl->count == DEBUGLIST_COUNT)
+			goto next;
+		for (i = 0; i < DEBUGLIST_COUNT; i++) {
+			if (dl->ptr[i] == NULL) {
+				dl->ptr[i] = ptr;
+				dl->size[i] = size;
+				dl->file[i] = file;
+				dl->line[i] = line;
+				dl->count++;
+				return;
+			}
+		}
+	next:
+		dl = ISC_LIST_NEXT(dl, link);
+	}
+
+	dl = malloc(sizeof(debuglink_t));
+	INSIST(dl != NULL);
+
+	ISC_LINK_INIT(dl, link);
+	for (i = 1; i < DEBUGLIST_COUNT; i++) {
+		dl->ptr[i] = NULL;
+		dl->size[i] = 0;
+		dl->file[i] = NULL;
+		dl->line[i] = 0;
+	}
+
+	dl->ptr[0] = ptr;
+	dl->size[0] = size;
+	dl->file[0] = file;
+	dl->line[0] = line;
+	dl->count = 1;
+
+	ISC_LIST_PREPEND(mctx->debuglist[size], dl, link);
+}
+
+static inline void
+delete_trace_entry(isc_mem_t *mctx, const void *ptr, unsigned int size,
+		   const char *file, unsigned int line)
+{
+	debuglink_t *dl;
+	unsigned int i;
+
+	if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0)
+		fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+					       ISC_MSG_DELTRACE,
+					       "del %p size %u "
+					       "file %s line %u mctx %p\n"),
+			ptr, size, file, line, mctx);
+
+	if (mctx->debuglist == NULL)
+		return;
+
+	if (size > mctx->max_size)
+		size = mctx->max_size;
+
+	dl = ISC_LIST_HEAD(mctx->debuglist[size]);
+	while (dl != NULL) {
+		for (i = 0; i < DEBUGLIST_COUNT; i++) {
+			if (dl->ptr[i] == ptr) {
+				dl->ptr[i] = NULL;
+				dl->size[i] = 0;
+				dl->file[i] = NULL;
+				dl->line[i] = 0;
+
+				INSIST(dl->count > 0);
+				dl->count--;
+				if (dl->count == 0) {
+					ISC_LIST_UNLINK(mctx->debuglist[size],
+							dl, link);
+					free(dl);
+				}
+				return;
+			}
+		}
+		dl = ISC_LIST_NEXT(dl, link);
+	}
+
+	/*
+	 * If we get here, we didn't find the item on the list.  We're
+	 * screwed.
+	 */
+	INSIST(dl != NULL);
+}
+#endif /* ISC_MEM_TRACKLINES */
+
+static inline size_t
+rmsize(size_t size) {
+	/*
+ 	 * round down to ALIGNMENT_SIZE
+	 */
+	return (size & (~(ALIGNMENT_SIZE - 1)));
+}
+
+static inline size_t
+quantize(size_t size) {
+	/*!
+	 * Round up the result in order to get a size big
+	 * enough to satisfy the request and be aligned on ALIGNMENT_SIZE
+	 * byte boundaries.
+	 */
+
+	if (size == 0U)
+		return (ALIGNMENT_SIZE);
+	return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1)));
+}
+
+static inline isc_boolean_t
+more_basic_blocks(isc_mem_t *ctx) {
+	void *new;
+	unsigned char *curr, *next;
+	unsigned char *first, *last;
+	unsigned char **table;
+	unsigned int table_size;
+	size_t increment;
+	int i;
+
+	/* Require: we hold the context lock. */
+
+	/*
+	 * Did we hit the quota for this context?
+	 */
+	increment = NUM_BASIC_BLOCKS * ctx->mem_target;
+	if (ctx->quota != 0U && ctx->total + increment > ctx->quota)
+		return (ISC_FALSE);
+
+	INSIST(ctx->basic_table_count <= ctx->basic_table_size);
+	if (ctx->basic_table_count == ctx->basic_table_size) {
+		table_size = ctx->basic_table_size + TABLE_INCREMENT;
+		table = (ctx->memalloc)(ctx->arg,
+					table_size * sizeof(unsigned char *));
+		if (table == NULL) {
+			ctx->memalloc_failures++;
+			return (ISC_FALSE);
+		}
+		if (ctx->basic_table_size != 0) {
+			memcpy(table, ctx->basic_table,
+			       ctx->basic_table_size *
+			       sizeof(unsigned char *));
+			(ctx->memfree)(ctx->arg, ctx->basic_table);
+		}
+		ctx->basic_table = table;
+		ctx->basic_table_size = table_size;
+	}
+
+	new = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target);
+	if (new == NULL) {
+		ctx->memalloc_failures++;
+		return (ISC_FALSE);
+	}
+	ctx->total += increment;
+	ctx->basic_table[ctx->basic_table_count] = new;
+	ctx->basic_table_count++;
+
+	curr = new;
+	next = curr + ctx->mem_target;
+	for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
+		((element *)curr)->next = (element *)next;
+		curr = next;
+		next += ctx->mem_target;
+	}
+	/*
+	 * curr is now pointing at the last block in the
+	 * array.
+	 */
+	((element *)curr)->next = NULL;
+	first = new;
+	last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1;
+	if (first < ctx->lowest || ctx->lowest == NULL)
+		ctx->lowest = first;
+	if (last > ctx->highest)
+		ctx->highest = last;
+	ctx->basic_blocks = new;
+
+	return (ISC_TRUE);
+}
+
+static inline isc_boolean_t
+more_frags(isc_mem_t *ctx, size_t new_size) {
+	int i, frags;
+	size_t total_size;
+	void *new;
+	unsigned char *curr, *next;
+
+	/*!
+	 * Try to get more fragments by chopping up a basic block.
+	 */
+
+	if (ctx->basic_blocks == NULL) {
+		if (!more_basic_blocks(ctx)) {
+			/*
+			 * We can't get more memory from the OS, or we've
+			 * hit the quota for this context.
+			 */
+			/*
+			 * XXXRTH  "At quota" notification here.
+			 */
+			return (ISC_FALSE);
+		}
+	}
+
+	total_size = ctx->mem_target;
+	new = ctx->basic_blocks;
+	ctx->basic_blocks = ctx->basic_blocks->next;
+	frags = total_size / new_size;
+	ctx->stats[new_size].blocks++;
+	ctx->stats[new_size].freefrags += frags;
+	/*
+	 * Set up a linked-list of blocks of size
+	 * "new_size".
+	 */
+	curr = new;
+	next = curr + new_size;
+	total_size -= new_size;
+	for (i = 0; i < (frags - 1); i++) {
+		((element *)curr)->next = (element *)next;
+		curr = next;
+		next += new_size;
+		total_size -= new_size;
+	}
+	/*
+	 * Add the remaining fragment of the basic block to a free list.
+	 */
+	total_size = rmsize(total_size);
+	if (total_size > 0U) {
+		((element *)next)->next = ctx->freelists[total_size];
+		ctx->freelists[total_size] = (element *)next;
+		ctx->stats[total_size].freefrags++;
+	}
+	/*
+	 * curr is now pointing at the last block in the
+	 * array.
+	 */
+	((element *)curr)->next = NULL;
+	ctx->freelists[new_size] = new;
+
+	return (ISC_TRUE);
+}
+
+static inline void *
+mem_getunlocked(isc_mem_t *ctx, size_t size) {
+	size_t new_size = quantize(size);
+	void *ret;
+
+	if (size >= ctx->max_size || new_size >= ctx->max_size) {
+		/*
+		 * memget() was called on something beyond our upper limit.
+		 */
+		if (ctx->quota != 0U && ctx->total + size > ctx->quota) {
+			ret = NULL;
+			goto done;
+		}
+		ret = (ctx->memalloc)(ctx->arg, size);
+		if (ret == NULL) {
+			ctx->memalloc_failures++;
+			goto done;
+		}
+		ctx->total += size;
+		ctx->inuse += size;
+		ctx->stats[ctx->max_size].gets++;
+		ctx->stats[ctx->max_size].totalgets++;
+		/*
+		 * If we don't set new_size to size, then the
+		 * ISC_MEM_FILL code might write over bytes we
+		 * don't own.
+		 */
+		new_size = size;
+		goto done;
+	}
+
+	/*
+	 * If there are no blocks in the free list for this size, get a chunk
+	 * of memory and then break it up into "new_size"-sized blocks, adding
+	 * them to the free list.
+	 */
+	if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size))
+		return (NULL);
+
+	/*
+	 * The free list uses the "rounded-up" size "new_size".
+	 */
+	ret = ctx->freelists[new_size];
+	ctx->freelists[new_size] = ctx->freelists[new_size]->next;
+
+	/*
+	 * The stats[] uses the _actual_ "size" requested by the
+	 * caller, with the caveat (in the code above) that "size" >= the
+	 * max. size (max_size) ends up getting recorded as a call to
+	 * max_size.
+	 */
+	ctx->stats[size].gets++;
+	ctx->stats[size].totalgets++;
+	ctx->stats[new_size].freefrags--;
+	ctx->inuse += new_size;
+
+ done:
+
+#if ISC_MEM_FILL
+	if (ret != NULL)
+		memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */
+#endif
+
+	return (ret);
+}
+
+#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN
+static inline void
+check_overrun(void *mem, size_t size, size_t new_size) {
+	unsigned char *cp;
+
+	cp = (unsigned char *)mem;
+	cp += size;
+	while (size < new_size) {
+		INSIST(*cp == 0xbe);
+		cp++;
+		size++;
+	}
+}
+#endif
+
+static inline void
+mem_putunlocked(isc_mem_t *ctx, void *mem, size_t size) {
+	size_t new_size = quantize(size);
+
+	if (size == ctx->max_size || new_size >= ctx->max_size) {
+		/*
+		 * memput() called on something beyond our upper limit.
+		 */
+#if ISC_MEM_FILL
+		memset(mem, 0xde, size); /* Mnemonic for "dead". */
+#endif
+		(ctx->memfree)(ctx->arg, mem);
+		INSIST(ctx->stats[ctx->max_size].gets != 0U);
+		ctx->stats[ctx->max_size].gets--;
+		INSIST(size <= ctx->total);
+		ctx->inuse -= size;
+		ctx->total -= size;
+		return;
+	}
+
+#if ISC_MEM_FILL
+#if ISC_MEM_CHECKOVERRUN
+	check_overrun(mem, size, new_size);
+#endif
+	memset(mem, 0xde, new_size); /* Mnemonic for "dead". */
+#endif
+
+	/*
+	 * The free list uses the "rounded-up" size "new_size".
+	 */
+	((element *)mem)->next = ctx->freelists[new_size];
+	ctx->freelists[new_size] = (element *)mem;
+
+	/*
+	 * The stats[] uses the _actual_ "size" requested by the
+	 * caller, with the caveat (in the code above) that "size" >= the
+	 * max. size (max_size) ends up getting recorded as a call to
+	 * max_size.
+	 */
+	INSIST(ctx->stats[size].gets != 0U);
+	ctx->stats[size].gets--;
+	ctx->stats[new_size].freefrags++;
+	ctx->inuse -= new_size;
+}
+
+/*!
+ * Perform a malloc, doing memory filling and overrun detection as necessary.
+ */
+static inline void *
+mem_get(isc_mem_t *ctx, size_t size) {
+	char *ret;
+
+#if ISC_MEM_CHECKOVERRUN
+	size += 1;
+#endif
+
+	ret = (ctx->memalloc)(ctx->arg, size);
+	if (ret == NULL)
+		ctx->memalloc_failures++;	
+
+#if ISC_MEM_FILL
+	if (ret != NULL)
+		memset(ret, 0xbe, size); /* Mnemonic for "beef". */
+#else
+#  if ISC_MEM_CHECKOVERRUN
+	if (ret != NULL)
+		ret[size-1] = 0xbe;
+#  endif
+#endif
+
+	return (ret);
+}
+
+/*!
+ * Perform a free, doing memory filling and overrun detection as necessary.
+ */
+static inline void
+mem_put(isc_mem_t *ctx, void *mem, size_t size) {
+#if ISC_MEM_CHECKOVERRUN
+	INSIST(((unsigned char *)mem)[size] == 0xbe);
+#endif
+#if ISC_MEM_FILL
+	memset(mem, 0xde, size); /* Mnemonic for "dead". */
+#else
+	UNUSED(size);
+#endif
+	(ctx->memfree)(ctx->arg, mem);
+}
+
+/*!
+ * Update internal counters after a memory get.
+ */
+static inline void
+mem_getstats(isc_mem_t *ctx, size_t size) {
+	ctx->total += size;
+	ctx->inuse += size;
+
+	if (size > ctx->max_size) {
+		ctx->stats[ctx->max_size].gets++;
+		ctx->stats[ctx->max_size].totalgets++;
+	} else {
+		ctx->stats[size].gets++;
+		ctx->stats[size].totalgets++;
+	}
+}
+
+/*!
+ * Update internal counters after a memory put.
+ */
+static inline void
+mem_putstats(isc_mem_t *ctx, void *ptr, size_t size) {
+	UNUSED(ptr);
+
+	INSIST(ctx->inuse >= size);
+	ctx->inuse -= size;
+
+	if (size > ctx->max_size) {
+		INSIST(ctx->stats[ctx->max_size].gets > 0U);
+		ctx->stats[ctx->max_size].gets--;
+	} else {
+		INSIST(ctx->stats[size].gets > 0U);
+		ctx->stats[size].gets--;
+	}
+}
+
+/*
+ * Private.
+ */
+
+static void *
+default_memalloc(void *arg, size_t size) {
+	UNUSED(arg);
+	if (size == 0U)
+		size = 1;
+	return (malloc(size));
+}
+
+static void
+default_memfree(void *arg, void *ptr) {
+	UNUSED(arg);
+	free(ptr);
+}
+
+static void
+initialize_action(void) {
+	RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS);
+	ISC_LIST_INIT(contexts);
+}
+
+/*
+ * Public.
+ */
+
+isc_result_t
+isc_mem_createx(size_t init_max_size, size_t target_size,
+		isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
+		isc_mem_t **ctxp)
+{
+	return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree,
+				 arg, ctxp, ISC_MEMFLAG_DEFAULT));
+				 
+}
+
+isc_result_t
+isc_mem_createx2(size_t init_max_size, size_t target_size,
+		 isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg,
+		 isc_mem_t **ctxp, unsigned int flags)
+{
+	isc_mem_t *ctx;
+	isc_result_t result;
+
+	REQUIRE(ctxp != NULL && *ctxp == NULL);
+	REQUIRE(memalloc != NULL);
+	REQUIRE(memfree != NULL);
+
+	INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0);
+
+	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+
+	ctx = (memalloc)(arg, sizeof(*ctx));
+	if (ctx == NULL)
+		return (ISC_R_NOMEMORY);
+
+	if ((flags & ISC_MEMFLAG_NOLOCK) == 0) {
+		result = isc_mutex_init(&ctx->lock);
+		if (result != ISC_R_SUCCESS) {
+			(memfree)(arg, ctx);
+			return (result);
+		}
+	}
+
+	if (init_max_size == 0U)
+		ctx->max_size = DEF_MAX_SIZE;
+	else
+		ctx->max_size = init_max_size;
+	ctx->flags = flags;
+	ctx->references = 1;
+	ctx->quota = 0;
+	ctx->total = 0;
+	ctx->inuse = 0;
+	ctx->maxinuse = 0;
+	ctx->hi_water = 0;
+	ctx->lo_water = 0;
+	ctx->hi_called = ISC_FALSE;
+	ctx->water = NULL;
+	ctx->water_arg = NULL;
+	ctx->magic = MEM_MAGIC;
+	isc_ondestroy_init(&ctx->ondestroy);
+	ctx->memalloc = memalloc;
+	ctx->memfree = memfree;
+	ctx->arg = arg;
+	ctx->stats = NULL;
+	ctx->checkfree = ISC_TRUE;
+#if ISC_MEM_TRACKLINES
+	ctx->debuglist = NULL;
+#endif
+	ISC_LIST_INIT(ctx->pools);
+	ctx->freelists = NULL;
+	ctx->basic_blocks = NULL;
+	ctx->basic_table = NULL;
+	ctx->basic_table_count = 0;
+	ctx->basic_table_size = 0;
+	ctx->lowest = NULL;
+	ctx->highest = NULL;
+
+	ctx->stats = (memalloc)(arg,
+				(ctx->max_size+1) * sizeof(struct stats));
+	if (ctx->stats == NULL) {
+		result = ISC_R_NOMEMORY;
+		goto error;
+	}
+	memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats));
+
+	if ((flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		if (target_size == 0U)
+			ctx->mem_target = DEF_MEM_TARGET;
+		else
+			ctx->mem_target = target_size;
+		ctx->freelists = (memalloc)(arg, ctx->max_size *
+						 sizeof(element *));
+		if (ctx->freelists == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto error;
+		}
+		memset(ctx->freelists, 0,
+		       ctx->max_size * sizeof(element *));
+	}
+
+#if ISC_MEM_TRACKLINES
+	if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) {
+		unsigned int i;
+
+		ctx->debuglist = (memalloc)(arg,
+				      (ctx->max_size+1) * sizeof(debuglist_t));
+		if (ctx->debuglist == NULL) {
+			result = ISC_R_NOMEMORY;
+			goto error;
+		}
+		for (i = 0; i <= ctx->max_size; i++)
+			ISC_LIST_INIT(ctx->debuglist[i]);
+	}
+#endif
+
+	ctx->memalloc_failures = 0;
+
+	LOCK(&lock);
+	ISC_LIST_INITANDAPPEND(contexts, ctx, link);
+	UNLOCK(&lock);
+
+	*ctxp = ctx;
+	return (ISC_R_SUCCESS);
+
+  error:
+	if (ctx != NULL) {
+		if (ctx->stats != NULL)
+			(memfree)(arg, ctx->stats);
+		if (ctx->freelists != NULL)
+			(memfree)(arg, ctx->freelists);
+#if ISC_MEM_TRACKLINES
+		if (ctx->debuglist != NULL)
+			(ctx->memfree)(ctx->arg, ctx->debuglist);
+#endif /* ISC_MEM_TRACKLINES */
+		if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
+			DESTROYLOCK(&ctx->lock);
+		(memfree)(arg, ctx);
+	}
+
+	return (result);
+}
+
+isc_result_t
+isc_mem_create(size_t init_max_size, size_t target_size,
+	       isc_mem_t **ctxp)
+{
+	return (isc_mem_createx2(init_max_size, target_size,
+				 default_memalloc, default_memfree, NULL,
+				 ctxp, ISC_MEMFLAG_DEFAULT));
+}
+
+isc_result_t
+isc_mem_create2(size_t init_max_size, size_t target_size,
+		isc_mem_t **ctxp, unsigned int flags)
+{
+	return (isc_mem_createx2(init_max_size, target_size,
+				 default_memalloc, default_memfree, NULL,
+				 ctxp, flags));
+}
+
+static void
+destroy(isc_mem_t *ctx) {
+	unsigned int i;
+	isc_ondestroy_t ondest;
+
+	ctx->magic = 0;
+
+	LOCK(&lock);
+	ISC_LIST_UNLINK(contexts, ctx, link);
+	UNLOCK(&lock);
+
+	INSIST(ISC_LIST_EMPTY(ctx->pools));
+
+#if ISC_MEM_TRACKLINES
+	if (ctx->debuglist != NULL) {
+		if (ctx->checkfree) {
+			for (i = 0; i <= ctx->max_size; i++) {
+				if (!ISC_LIST_EMPTY(ctx->debuglist[i]))
+					print_active(ctx, stderr);
+				INSIST(ISC_LIST_EMPTY(ctx->debuglist[i]));
+			}
+		} else {
+			debuglink_t *dl;
+
+			for (i = 0; i <= ctx->max_size; i++)
+				for (dl = ISC_LIST_HEAD(ctx->debuglist[i]);
+				     dl != NULL;
+				     dl = ISC_LIST_HEAD(ctx->debuglist[i])) {
+					ISC_LIST_UNLINK(ctx->debuglist[i],
+						 	dl, link);
+					free(dl);
+				}
+		}
+		(ctx->memfree)(ctx->arg, ctx->debuglist);
+	}
+#endif
+	INSIST(ctx->references == 0);
+
+	if (ctx->checkfree) {
+		for (i = 0; i <= ctx->max_size; i++) {
+#if ISC_MEM_TRACKLINES
+			if (ctx->stats[i].gets != 0U)
+				print_active(ctx, stderr);
+#endif
+			INSIST(ctx->stats[i].gets == 0U);
+		}
+	}
+
+	(ctx->memfree)(ctx->arg, ctx->stats);
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		for (i = 0; i < ctx->basic_table_count; i++)
+			(ctx->memfree)(ctx->arg, ctx->basic_table[i]);
+		(ctx->memfree)(ctx->arg, ctx->freelists);
+		(ctx->memfree)(ctx->arg, ctx->basic_table);
+	}
+
+	ondest = ctx->ondestroy;
+
+	if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0)
+		DESTROYLOCK(&ctx->lock);
+	(ctx->memfree)(ctx->arg, ctx);
+
+	isc_ondestroy_notify(&ondest, ctx);
+}
+
+void
+isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) {
+	REQUIRE(VALID_CONTEXT(source));
+	REQUIRE(targetp != NULL && *targetp == NULL);
+
+	MCTXLOCK(source, &source->lock);
+	source->references++;
+	MCTXUNLOCK(source, &source->lock);
+
+	*targetp = source;
+}
+
+void
+isc_mem_detach(isc_mem_t **ctxp) {
+	isc_mem_t *ctx;
+	isc_boolean_t want_destroy = ISC_FALSE;
+
+	REQUIRE(ctxp != NULL);
+	ctx = *ctxp;
+	REQUIRE(VALID_CONTEXT(ctx));
+
+	MCTXLOCK(ctx, &ctx->lock);
+	INSIST(ctx->references > 0);
+	ctx->references--;
+	if (ctx->references == 0)
+		want_destroy = ISC_TRUE;
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (want_destroy)
+		destroy(ctx);
+
+	*ctxp = NULL;
+}
+
+/*
+ * isc_mem_putanddetach() is the equivalent of:
+ *
+ * mctx = NULL;
+ * isc_mem_attach(ptr->mctx, &mctx);
+ * isc_mem_detach(&ptr->mctx);
+ * isc_mem_put(mctx, ptr, sizeof(*ptr);
+ * isc_mem_detach(&mctx);
+ */
+
+void
+isc__mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) {
+	isc_mem_t *ctx;
+	isc_boolean_t want_destroy = ISC_FALSE;
+	size_info *si;
+	size_t oldsize;
+
+	REQUIRE(ctxp != NULL);
+	ctx = *ctxp;
+	REQUIRE(VALID_CONTEXT(ctx));
+	REQUIRE(ptr != NULL);
+
+	/*
+	 * Must be before mem_putunlocked() as ctxp is usually within
+	 * [ptr..ptr+size).
+	 */
+	*ctxp = NULL;
+
+	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
+		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
+			si = &(((size_info *)ptr)[-1]);
+			oldsize = si->u.size - ALIGNMENT_SIZE;
+			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
+				oldsize -= ALIGNMENT_SIZE;
+			INSIST(oldsize == size);
+		}
+		isc__mem_free(ctx, ptr FLARG_PASS);
+
+		MCTXLOCK(ctx, &ctx->lock);
+		ctx->references--;
+		if (ctx->references == 0)
+			want_destroy = ISC_TRUE;
+		MCTXUNLOCK(ctx, &ctx->lock);
+		if (want_destroy)
+			destroy(ctx);
+
+		return;
+	}
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putunlocked(ctx, ptr, size);
+	} else {
+		mem_put(ctx, ptr, size);
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putstats(ctx, ptr, size);
+	}
+
+	DELETE_TRACE(ctx, ptr, size, file, line);
+	INSIST(ctx->references > 0);
+	ctx->references--;
+	if (ctx->references == 0)
+		want_destroy = ISC_TRUE;
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (want_destroy)
+		destroy(ctx);
+}
+
+void
+isc_mem_destroy(isc_mem_t **ctxp) {
+	isc_mem_t *ctx;
+
+	/*
+	 * This routine provides legacy support for callers who use mctxs
+	 * without attaching/detaching.
+	 */
+
+	REQUIRE(ctxp != NULL);
+	ctx = *ctxp;
+	REQUIRE(VALID_CONTEXT(ctx));
+
+	MCTXLOCK(ctx, &ctx->lock);
+#if ISC_MEM_TRACKLINES
+	if (ctx->references != 1)
+		print_active(ctx, stderr);
+#endif
+	REQUIRE(ctx->references == 1);
+	ctx->references--;
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	destroy(ctx);
+
+	*ctxp = NULL;
+}
+
+isc_result_t
+isc_mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event) {
+	isc_result_t res;
+
+	MCTXLOCK(ctx, &ctx->lock);
+	res = isc_ondestroy_register(&ctx->ondestroy, task, event);
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	return (res);
+}
+
+
+void *
+isc__mem_get(isc_mem_t *ctx, size_t size FLARG) {
+	void *ptr;
+	isc_boolean_t call_water = ISC_FALSE;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+
+	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0)
+		return (isc__mem_allocate(ctx, size FLARG_PASS));
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		MCTXLOCK(ctx, &ctx->lock);
+		ptr = mem_getunlocked(ctx, size);
+	} else {
+		ptr = mem_get(ctx, size);
+		MCTXLOCK(ctx, &ctx->lock);
+		if (ptr != NULL)
+			mem_getstats(ctx, size);
+	}
+
+	ADD_TRACE(ctx, ptr, size, file, line);
+	if (ctx->hi_water != 0U && !ctx->hi_called &&
+	    ctx->inuse > ctx->hi_water) {
+		ctx->hi_called = ISC_TRUE;
+		call_water = ISC_TRUE;
+	}
+	if (ctx->inuse > ctx->maxinuse) {
+		ctx->maxinuse = ctx->inuse;
+		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
+		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
+			fprintf(stderr, "maxinuse = %lu\n",
+				(unsigned long)ctx->inuse);
+	}
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (call_water)
+		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
+
+	return (ptr);
+}
+
+void
+isc__mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG)
+{
+	isc_boolean_t call_water = ISC_FALSE;
+	size_info *si;
+	size_t oldsize;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	REQUIRE(ptr != NULL);
+
+	if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) {
+		if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) {
+			si = &(((size_info *)ptr)[-1]);
+			oldsize = si->u.size - ALIGNMENT_SIZE;
+			if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
+				oldsize -= ALIGNMENT_SIZE;
+			INSIST(oldsize == size);
+		}
+		isc__mem_free(ctx, ptr FLARG_PASS);
+		return;
+	}
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putunlocked(ctx, ptr, size);
+	} else {
+		mem_put(ctx, ptr, size);
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putstats(ctx, ptr, size);
+	}
+
+	DELETE_TRACE(ctx, ptr, size, file, line);
+
+	/*
+	 * The check against ctx->lo_water == 0 is for the condition
+	 * when the context was pushed over hi_water but then had
+	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
+	 */
+	if (ctx->hi_called && 
+	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
+		ctx->hi_called = ISC_FALSE;
+
+		if (ctx->water != NULL)
+			call_water = ISC_TRUE;
+	}
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (call_water)
+		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
+}
+
+#if ISC_MEM_TRACKLINES
+static void
+print_active(isc_mem_t *mctx, FILE *out) {
+	if (mctx->debuglist != NULL) {
+		debuglink_t *dl;
+		unsigned int i, j;
+		const char *format;
+		isc_boolean_t found;
+
+		fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+					    ISC_MSG_DUMPALLOC,
+					    "Dump of all outstanding "
+					    "memory allocations:\n"));
+		found = ISC_FALSE;
+		format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				        ISC_MSG_PTRFILELINE,
+					"\tptr %p size %u file %s line %u\n");
+		for (i = 0; i <= mctx->max_size; i++) {
+			dl = ISC_LIST_HEAD(mctx->debuglist[i]);
+			
+			if (dl != NULL)
+				found = ISC_TRUE;
+
+			while (dl != NULL) {
+				for (j = 0; j < DEBUGLIST_COUNT; j++)
+					if (dl->ptr[j] != NULL)
+						fprintf(out, format,
+							dl->ptr[j],
+							dl->size[j],
+							dl->file[j],
+							dl->line[j]);
+				dl = ISC_LIST_NEXT(dl, link);
+			}
+		}
+		if (!found)
+			fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+						    ISC_MSG_NONE, "\tNone.\n"));
+	}
+}
+#endif
+
+/*
+ * Print the stats[] on the stream "out" with suitable formatting.
+ */
+void
+isc_mem_stats(isc_mem_t *ctx, FILE *out) {
+	size_t i;
+	const struct stats *s;
+	const isc_mempool_t *pool;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	MCTXLOCK(ctx, &ctx->lock);
+
+	for (i = 0; i <= ctx->max_size; i++) {
+		s = &ctx->stats[i];
+
+		if (s->totalgets == 0U && s->gets == 0U)
+			continue;
+		fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
+			(i == ctx->max_size) ? ">=" : "  ",
+			(unsigned long) i, s->totalgets, s->gets);
+		if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 &&
+		    (s->blocks != 0U || s->freefrags != 0U))
+			fprintf(out, " (%lu bl, %lu ff)",
+				s->blocks, s->freefrags);
+		fputc('\n', out);
+	}
+
+	/*
+	 * Note that since a pool can be locked now, these stats might be
+	 * somewhat off if the pool is in active use at the time the stats
+	 * are dumped.  The link fields are protected by the isc_mem_t's
+	 * lock, however, so walking this list and extracting integers from
+	 * stats fields is always safe.
+	 */
+	pool = ISC_LIST_HEAD(ctx->pools);
+	if (pool != NULL) {
+		fprintf(out, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+					    ISC_MSG_POOLSTATS,
+					    "[Pool statistics]\n"));
+		fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n",
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLNAME, "name"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLSIZE, "size"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLMAXALLOC, "maxalloc"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLALLOCATED, "allocated"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLFREECOUNT, "freecount"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLFREEMAX, "freemax"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLFILLCOUNT, "fillcount"),
+			isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM,
+				       ISC_MSG_POOLGETS, "gets"),
+			"L");
+	}
+	while (pool != NULL) {
+		fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n",
+			pool->name, (unsigned long) pool->size, pool->maxalloc,
+			pool->allocated, pool->freecount, pool->freemax,
+			pool->fillcount, pool->gets,
+			(pool->lock == NULL ? "N" : "Y"));
+		pool = ISC_LIST_NEXT(pool, link);
+	}
+
+#if ISC_MEM_TRACKLINES
+	print_active(ctx, out);
+#endif
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+}
+
+/*
+ * Replacements for malloc() and free() -- they implicitly remember the
+ * size of the object allocated (with some additional overhead).
+ */
+
+static void *
+isc__mem_allocateunlocked(isc_mem_t *ctx, size_t size) {
+	size_info *si;
+
+	size += ALIGNMENT_SIZE;
+	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0)
+		size += ALIGNMENT_SIZE;
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0)
+		si = mem_getunlocked(ctx, size);
+	else
+		si = mem_get(ctx, size);
+
+	if (si == NULL)
+		return (NULL);
+	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+		si->u.ctx = ctx;
+		si++;
+	}
+	si->u.size = size;
+	return (&si[1]);
+}
+
+void *
+isc__mem_allocate(isc_mem_t *ctx, size_t size FLARG) {
+	size_info *si;
+	isc_boolean_t call_water = ISC_FALSE;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		MCTXLOCK(ctx, &ctx->lock);
+		si = isc__mem_allocateunlocked(ctx, size);
+	} else {
+		si = isc__mem_allocateunlocked(ctx, size);
+		MCTXLOCK(ctx, &ctx->lock);
+		if (si != NULL)
+			mem_getstats(ctx, si[-1].u.size);
+	}
+
+#if ISC_MEM_TRACKLINES
+	ADD_TRACE(ctx, si, si[-1].u.size, file, line);
+#endif
+	if (ctx->hi_water != 0U && !ctx->hi_called &&
+	    ctx->inuse > ctx->hi_water) {
+		ctx->hi_called = ISC_TRUE;
+		call_water = ISC_TRUE;
+	}
+	if (ctx->inuse > ctx->maxinuse) {
+		ctx->maxinuse = ctx->inuse;
+		if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water &&
+		    (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0)
+			fprintf(stderr, "maxinuse = %lu\n",
+				(unsigned long)ctx->inuse);
+	}
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (call_water)
+		(ctx->water)(ctx->water_arg, ISC_MEM_HIWATER);
+
+	return (si);
+}
+
+void
+isc__mem_free(isc_mem_t *ctx, void *ptr FLARG) {
+	size_info *si;
+	size_t size;
+	isc_boolean_t call_water= ISC_FALSE;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	REQUIRE(ptr != NULL);
+
+	if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) {
+		si = &(((size_info *)ptr)[-2]);
+		REQUIRE(si->u.ctx == ctx);
+		size = si[1].u.size;
+	} else {
+		si = &(((size_info *)ptr)[-1]);
+		size = si->u.size;
+	}
+
+	if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putunlocked(ctx, si, size);
+	} else {
+		mem_put(ctx, si, size);
+		MCTXLOCK(ctx, &ctx->lock);
+		mem_putstats(ctx, si, size);
+	}
+
+	DELETE_TRACE(ctx, ptr, size, file, line);
+
+	/*
+	 * The check against ctx->lo_water == 0 is for the condition
+	 * when the context was pushed over hi_water but then had
+	 * isc_mem_setwater() called with 0 for hi_water and lo_water.
+	 */
+	if (ctx->hi_called && 
+	    (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) {
+		ctx->hi_called = ISC_FALSE;
+
+		if (ctx->water != NULL)
+			call_water = ISC_TRUE;
+	}
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	if (call_water)
+		(ctx->water)(ctx->water_arg, ISC_MEM_LOWATER);
+}
+
+
+/*
+ * Other useful things.
+ */
+
+char *
+isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) {
+	size_t len;
+	char *ns;
+
+	REQUIRE(VALID_CONTEXT(mctx));
+	REQUIRE(s != NULL);
+
+	len = strlen(s);
+
+	ns = isc__mem_allocate(mctx, len + 1 FLARG_PASS);
+
+	if (ns != NULL)
+		strncpy(ns, s, len + 1);
+
+	return (ns);
+}
+
+void
+isc_mem_setdestroycheck(isc_mem_t *ctx, isc_boolean_t flag) {
+	REQUIRE(VALID_CONTEXT(ctx));
+	MCTXLOCK(ctx, &ctx->lock);
+
+	ctx->checkfree = flag;
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+}
+
+/*
+ * Quotas
+ */
+
+void
+isc_mem_setquota(isc_mem_t *ctx, size_t quota) {
+	REQUIRE(VALID_CONTEXT(ctx));
+	MCTXLOCK(ctx, &ctx->lock);
+
+	ctx->quota = quota;
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+}
+
+size_t
+isc_mem_getquota(isc_mem_t *ctx) {
+	size_t quota;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	MCTXLOCK(ctx, &ctx->lock);
+
+	quota = ctx->quota;
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	return (quota);
+}
+
+size_t
+isc_mem_inuse(isc_mem_t *ctx) {
+	size_t inuse;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	MCTXLOCK(ctx, &ctx->lock);
+
+	inuse = ctx->inuse;
+
+	MCTXUNLOCK(ctx, &ctx->lock);
+
+	return (inuse);
+}
+
+void
+isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg,
+                 size_t hiwater, size_t lowater)
+{
+	isc_boolean_t callwater = ISC_FALSE;
+	isc_mem_water_t oldwater;
+	void *oldwater_arg;
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	REQUIRE(hiwater >= lowater);
+
+	MCTXLOCK(ctx, &ctx->lock);
+	oldwater = ctx->water;
+	oldwater_arg = ctx->water_arg;
+	if (water == NULL) {
+		callwater = ctx->hi_called;
+		ctx->water = NULL;
+		ctx->water_arg = NULL;
+		ctx->hi_water = 0;
+		ctx->lo_water = 0;
+		ctx->hi_called = ISC_FALSE;
+	} else {
+		if (ctx->hi_called &&
+		    (ctx->water != water || ctx->water_arg != water_arg ||
+		     ctx->inuse < lowater || lowater == 0U))
+			callwater = ISC_TRUE;
+		ctx->water = water;
+		ctx->water_arg = water_arg;
+		ctx->hi_water = hiwater;
+		ctx->lo_water = lowater;
+		ctx->hi_called = ISC_FALSE;
+	}
+	MCTXUNLOCK(ctx, &ctx->lock);
+	
+	if (callwater && oldwater != NULL)
+		(oldwater)(oldwater_arg, ISC_MEM_LOWATER);
+}
+
+/*
+ * Memory pool stuff
+ */
+
+isc_result_t
+isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) {
+	isc_mempool_t *mpctx;
+
+	REQUIRE(VALID_CONTEXT(mctx));
+	REQUIRE(size > 0U);
+	REQUIRE(mpctxp != NULL && *mpctxp == NULL);
+
+	/*
+	 * Allocate space for this pool, initialize values, and if all works
+	 * well, attach to the memory context.
+	 */
+	mpctx = isc_mem_get(mctx, sizeof(isc_mempool_t));
+	if (mpctx == NULL)
+		return (ISC_R_NOMEMORY);
+
+	mpctx->magic = MEMPOOL_MAGIC;
+	mpctx->lock = NULL;
+	mpctx->mctx = mctx;
+	mpctx->size = size;
+	mpctx->maxalloc = UINT_MAX;
+	mpctx->allocated = 0;
+	mpctx->freecount = 0;
+	mpctx->freemax = 1;
+	mpctx->fillcount = 1;
+	mpctx->gets = 0;
+#if ISC_MEMPOOL_NAMES
+	mpctx->name[0] = 0;
+#endif
+	mpctx->items = NULL;
+
+	*mpctxp = mpctx;
+
+	MCTXLOCK(mctx, &mctx->lock);
+	ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link);
+	MCTXUNLOCK(mctx, &mctx->lock);
+
+	return (ISC_R_SUCCESS);
+}
+
+void
+isc_mempool_setname(isc_mempool_t *mpctx, const char *name) {
+	REQUIRE(name != NULL);
+
+#if ISC_MEMPOOL_NAMES
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	strncpy(mpctx->name, name, sizeof(mpctx->name) - 1);
+	mpctx->name[sizeof(mpctx->name) - 1] = '\0';
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+#else
+	UNUSED(mpctx);
+	UNUSED(name);
+#endif
+}
+
+void
+isc_mempool_destroy(isc_mempool_t **mpctxp) {
+	isc_mempool_t *mpctx;
+	isc_mem_t *mctx;
+	isc_mutex_t *lock;
+	element *item;
+
+	REQUIRE(mpctxp != NULL);
+	mpctx = *mpctxp;
+	REQUIRE(VALID_MEMPOOL(mpctx));
+#if ISC_MEMPOOL_NAMES
+	if (mpctx->allocated > 0)
+		UNEXPECTED_ERROR(__FILE__, __LINE__,
+				 "isc_mempool_destroy(): mempool %s "
+				 "leaked memory",
+				 mpctx->name);
+#endif
+	REQUIRE(mpctx->allocated == 0);
+
+	mctx = mpctx->mctx;
+
+	lock = mpctx->lock;
+
+	if (lock != NULL)
+		LOCK(lock);
+
+	/*
+	 * Return any items on the free list
+	 */
+	MCTXLOCK(mctx, &mctx->lock);
+	while (mpctx->items != NULL) {
+		INSIST(mpctx->freecount > 0);
+		mpctx->freecount--;
+		item = mpctx->items;
+		mpctx->items = item->next;
+
+		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+			mem_putunlocked(mctx, item, mpctx->size);
+		} else {
+			mem_put(mctx, item, mpctx->size);
+			mem_putstats(mctx, item, mpctx->size);
+		}
+	}
+	MCTXUNLOCK(mctx, &mctx->lock);
+
+	/*
+	 * Remove our linked list entry from the memory context.
+	 */
+	MCTXLOCK(mctx, &mctx->lock);
+	ISC_LIST_UNLINK(mctx->pools, mpctx, link);
+	MCTXUNLOCK(mctx, &mctx->lock);
+
+	mpctx->magic = 0;
+
+	isc_mem_put(mpctx->mctx, mpctx, sizeof(isc_mempool_t));
+
+	if (lock != NULL)
+		UNLOCK(lock);
+
+	*mpctxp = NULL;
+}
+
+void
+isc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) {
+	REQUIRE(VALID_MEMPOOL(mpctx));
+	REQUIRE(mpctx->lock == NULL);
+	REQUIRE(lock != NULL);
+
+	mpctx->lock = lock;
+}
+
+void *
+isc__mempool_get(isc_mempool_t *mpctx FLARG) {
+	element *item;
+	isc_mem_t *mctx;
+	unsigned int i;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	mctx = mpctx->mctx;
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	/*
+	 * Don't let the caller go over quota
+	 */
+	if (mpctx->allocated >= mpctx->maxalloc) {
+		item = NULL;
+		goto out;
+	}
+
+	/*
+	 * if we have a free list item, return the first here
+	 */
+	item = mpctx->items;
+	if (item != NULL) {
+		mpctx->items = item->next;
+		INSIST(mpctx->freecount > 0);
+		mpctx->freecount--;
+		mpctx->gets++;
+		mpctx->allocated++;
+		goto out;
+	}
+
+	/*
+	 * We need to dip into the well.  Lock the memory context here and
+	 * fill up our free list.
+	 */
+	MCTXLOCK(mctx, &mctx->lock);
+	for (i = 0; i < mpctx->fillcount; i++) {
+		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+			item = mem_getunlocked(mctx, mpctx->size);
+		} else {
+			item = mem_get(mctx, mpctx->size);
+			if (item != NULL)
+				mem_getstats(mctx, mpctx->size);
+		}
+		if (item == NULL)
+			break;
+		item->next = mpctx->items;
+		mpctx->items = item;
+		mpctx->freecount++;
+	}
+	MCTXUNLOCK(mctx, &mctx->lock);
+
+	/*
+	 * If we didn't get any items, return NULL.
+	 */
+	item = mpctx->items;
+	if (item == NULL)
+		goto out;
+
+	mpctx->items = item->next;
+	mpctx->freecount--;
+	mpctx->gets++;
+	mpctx->allocated++;
+
+ out:
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+#if ISC_MEM_TRACKLINES
+	if (item != NULL) {
+		MCTXLOCK(mctx, &mctx->lock);
+		ADD_TRACE(mctx, item, mpctx->size, file, line);
+		MCTXUNLOCK(mctx, &mctx->lock);
+	}
+#endif /* ISC_MEM_TRACKLINES */
+
+	return (item);
+}
+
+void
+isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) {
+	isc_mem_t *mctx;
+	element *item;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+	REQUIRE(mem != NULL);
+
+	mctx = mpctx->mctx;
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	INSIST(mpctx->allocated > 0);
+	mpctx->allocated--;
+
+#if ISC_MEM_TRACKLINES
+	MCTXLOCK(mctx, &mctx->lock);
+	DELETE_TRACE(mctx, mem, mpctx->size, file, line);
+	MCTXUNLOCK(mctx, &mctx->lock);
+#endif /* ISC_MEM_TRACKLINES */
+
+	/*
+	 * If our free list is full, return this to the mctx directly.
+	 */
+	if (mpctx->freecount >= mpctx->freemax) {
+		if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) {
+			MCTXLOCK(mctx, &mctx->lock);
+			mem_putunlocked(mctx, mem, mpctx->size);
+			MCTXUNLOCK(mctx, &mctx->lock);
+		} else {
+			mem_put(mctx, mem, mpctx->size);
+			MCTXLOCK(mctx, &mctx->lock);
+			mem_putstats(mctx, mem, mpctx->size);
+			MCTXUNLOCK(mctx, &mctx->lock);
+		}
+		if (mpctx->lock != NULL)
+			UNLOCK(mpctx->lock);
+		return;
+	}
+
+	/*
+	 * Otherwise, attach it to our free list and bump the counter.
+	 */
+	mpctx->freecount++;
+	item = (element *)mem;
+	item->next = mpctx->items;
+	mpctx->items = item;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+}
+
+/*
+ * Quotas
+ */
+
+void
+isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) {
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	mpctx->freemax = limit;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+}
+
+unsigned int
+isc_mempool_getfreemax(isc_mempool_t *mpctx) {
+	unsigned int freemax;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	freemax = mpctx->freemax;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+	return (freemax);
+}
+
+unsigned int
+isc_mempool_getfreecount(isc_mempool_t *mpctx) {
+	unsigned int freecount;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	freecount = mpctx->freecount;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+	return (freecount);
+}
+
+void
+isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) {
+	REQUIRE(limit > 0);
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	mpctx->maxalloc = limit;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+}
+
+unsigned int
+isc_mempool_getmaxalloc(isc_mempool_t *mpctx) {
+	unsigned int maxalloc;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	maxalloc = mpctx->maxalloc;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+	return (maxalloc);
+}
+
+unsigned int
+isc_mempool_getallocated(isc_mempool_t *mpctx) {
+	unsigned int allocated;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	allocated = mpctx->allocated;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+	return (allocated);
+}
+
+void
+isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) {
+	REQUIRE(limit > 0);
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	mpctx->fillcount = limit;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+}
+
+unsigned int
+isc_mempool_getfillcount(isc_mempool_t *mpctx) {
+	unsigned int fillcount;
+
+	REQUIRE(VALID_MEMPOOL(mpctx));
+
+	if (mpctx->lock != NULL)
+		LOCK(mpctx->lock);
+
+	fillcount = mpctx->fillcount;
+
+	if (mpctx->lock != NULL)
+		UNLOCK(mpctx->lock);
+
+	return (fillcount);
+}
+
+void
+isc_mem_printactive(isc_mem_t *ctx, FILE *file) {
+
+	REQUIRE(VALID_CONTEXT(ctx));
+	REQUIRE(file != NULL);
+
+#if !ISC_MEM_TRACKLINES
+	UNUSED(ctx);
+	UNUSED(file);
+#else
+	print_active(ctx, file);
+#endif
+}
+
+void
+isc_mem_printallactive(FILE *file) {
+#if !ISC_MEM_TRACKLINES
+	UNUSED(file);
+#else
+	isc_mem_t *ctx;
+
+	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+
+	LOCK(&lock);
+	for (ctx = ISC_LIST_HEAD(contexts);
+	     ctx != NULL;
+	     ctx = ISC_LIST_NEXT(ctx, link)) {
+		fprintf(file, "context: %p\n", ctx);
+		print_active(ctx, file);
+	}
+	UNLOCK(&lock);
+#endif
+}
+
+void    
+isc_mem_checkdestroyed(FILE *file) {
+
+	RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS);
+
+	LOCK(&lock);
+	if (!ISC_LIST_EMPTY(contexts))  {
+#if ISC_MEM_TRACKLINES
+		isc_mem_t *ctx;
+
+		for (ctx = ISC_LIST_HEAD(contexts);
+		     ctx != NULL;
+		     ctx = ISC_LIST_NEXT(ctx, link)) {
+			fprintf(file, "context: %p\n", ctx);
+			print_active(ctx, file);
+		}
+		fflush(file);
+#endif
+		INSIST(0);
+	}
+	UNLOCK(&lock);
+}
-- 
cgit v1.1