/*
 * nasd_freelist.h
 *
 * Keep an arena of fixed-size objects. When a new object is needed,
 * allocate it as necessary. When an object is freed, either put it
 * in the arena, or really free it, depending on the maximum arena
 * size.
 *
 * Author: Jim Zelenka
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1995,1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#ifndef _NASD_FREELIST_H_
#define _NASD_FREELIST_H_

#include <nasd/nasd_options.h>
#include <nasd/nasd_itypes.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_mem.h>

#if NASD_FREELIST_STATS > 0

typedef struct nasd_freelist_stats_s {
  char  *file;
  int    line;
  int    allocations;
  int    frees;
  int    max_free;
  int    grows;
  int    outstanding;
  int    max_outstanding;
} nasd_freelist_stats_t;

#define NASD_FREELIST_STAT_INIT(_fl_) { \
	bzero((char *)&((_fl_)->stats), sizeof(nasd_freelist_stats_t)); \
	(_fl_)->stats.file = __FILE__; \
	(_fl_)->stats.line = __LINE__; \
}

#define NASD_FREELIST_STAT_ALLOC(_fl_) { \
	(_fl_)->stats.allocations++; \
	(_fl_)->stats.outstanding++; \
	if ((_fl_)->stats.outstanding > (_fl_)->stats.max_outstanding) \
		(_fl_)->stats.max_outstanding = (_fl_)->stats.outstanding; \
}

#define NASD_FREELIST_STAT_FREE_UPDATE(_fl_) { \
	if ((_fl_)->free_cnt > (_fl_)->stats.max_free) \
		(_fl_)->stats.max_free = (_fl_)->free_cnt; \
}

#define NASD_FREELIST_STAT_FREE(_fl_) { \
	(_fl_)->stats.frees++; \
	(_fl_)->stats.outstanding--; \
	NASD_FREELIST_STAT_FREE_UPDATE(_fl_); \
}

#define NASD_FREELIST_STAT_GROW(_fl_) { \
	(_fl_)->stats.grows++; \
	NASD_FREELIST_STAT_FREE_UPDATE(_fl_); \
}

#define NASD_FREELIST_STAT_REPORT(_fl_) { \
	nasd_printf("Freelist at %s %d (%s)\n", (_fl_)->stats.file, (_fl_)->stats.line, NASD_STRING(_fl_)); \
	nasd_printf("  %d allocations, %d frees\n", (_fl_)->stats.allocations, (_fl_)->stats.frees); \
	nasd_printf("  %d grows\n", (_fl_)->stats.grows); \
	nasd_printf("  %d outstanding\n", (_fl_)->stats.outstanding); \
	nasd_printf("  %d free (max)\n", (_fl_)->stats.max_free); \
	nasd_printf("  %d outstanding (max)\n", (_fl_)->stats.max_outstanding); \
}

#else /* NASD_FREELIST_STATS > 0 */

#define NASD_FREELIST_STAT_INIT(_fl_)
#define NASD_FREELIST_STAT_ALLOC(_fl_)
#define NASD_FREELIST_STAT_FREE_UPDATE(_fl_)
#define NASD_FREELIST_STAT_FREE(_fl_)
#define NASD_FREELIST_STAT_GROW(_fl_)
#define NASD_FREELIST_STAT_REPORT(_fl_)

#endif /* NASD_FREELIST_STATS > 0 */

struct nasd_freelist_s {
	void  *objlist;      /* list of free obj */
	int    free_cnt;     /* how many free obj */
	int    max_free_cnt; /* max free arena size */
	int    obj_inc;      /* how many to allocate at a time */
	int    obj_size;     /* size of objects */
	NASD_DECLARE_MUTEX(lock)
#if NASD_FREELIST_STATS > 0
	nasd_freelist_stats_t  stats;  /* statistics */
#endif /* NASD_FREELIST_STATS > 0 */
};

/*
 * fl     = freelist
 * maxcnt = max number of items in arena
 * inc    = how many to allocate at a time
 * size   = size of object
 */
#define NASD_FREELIST_CREATE(_fl_,_maxcnt_,_inc_,_size_) { \
  int rc; \
  NASD_ASSERT((_inc_) > 0); \
  NASD_Malloc(_fl_, sizeof(nasd_freelist_t), (nasd_freelist_t *)); \
  if (_fl_) { \
    (_fl_)->objlist = NULL; \
    (_fl_)->free_cnt = 0; \
    (_fl_)->max_free_cnt = _maxcnt_; \
    (_fl_)->obj_inc = _inc_; \
    (_fl_)->obj_size = _size_; \
    rc = nasd_mutex_init(&(_fl_)->lock); \
    if (rc) { \
      NASD_Free(_fl_, sizeof(nasd_freelist_t)); \
      _fl_ = NULL; \
    } \
    else { \
      NASD_FREELIST_STAT_INIT(_fl_); \
    } \
  } \
}

/*
 * fl    = freelist
 * cnt   = number to prime with
 * nextp = name of "next" pointer in obj
 * cast  = object cast
 */
#define NASD_FREELIST_PRIME(_fl_,_cnt_,_nextp_,_cast_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	for(_i=0;_i<(_cnt_);_i++) { \
		NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
		if (_p) { \
			(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
			(_fl_)->objlist = _p; \
			(_fl_)->free_cnt++; \
		} \
		else { \
			break; \
		} \
	} \
	NASD_FREELIST_STAT_FREE_UPDATE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

#define NASD_FREELIST_MUTEX_OF(_fl_) ((_fl_)->lock)

#define NASD_FREELIST_DO_UNLOCK(_fl_) { \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

#define NASD_FREELIST_DO_LOCK(_fl_) { \
	NASD_LOCK_MUTEX((_fl_)->lock); \
}

/* assumes lock held */
#define NASD_FREELIST_ISEMPTY(_fl_) ((_fl_)->objlist == NULL)

/*
 * fl    = freelist
 * cnt   = number to prime with
 * nextp = name of "next" pointer in obj
 * cast  = object cast
 * init  = func to call to init obj
 */
#define NASD_FREELIST_PRIME_INIT(_fl_,_cnt_,_nextp_,_cast_,_init_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	for(_i=0;_i<(_cnt_);_i++) { \
		NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
		if (_p) { \
			if (_init_ (_cast_ _p)) { \
				NASD_Free(_p,(_fl_)->obj_size); \
				_p = NULL; \
			} \
			else { \
				(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
				(_fl_)->objlist = _p; \
				(_fl_)->free_cnt++; \
			} \
		} \
		else { \
			break; \
		} \
	} \
	NASD_FREELIST_STAT_FREE_UPDATE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * cnt   = number to prime with
 * nextp = name of "next" pointer in obj
 * cast  = object cast
 * init  = func to call to init obj
 * arg   = arg to init obj func
 */
#define NASD_FREELIST_PRIME_INIT_ARG(_fl_,_cnt_,_nextp_,_cast_,_init_,_arg_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	for(_i=0;_i<(_cnt_);_i++) { \
		NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
		if (_p) { \
			if (_init_ (_cast_ _p,_arg_)) { \
				NASD_Free(_p,(_fl_)->obj_size); \
				_p = NULL; \
			} \
			else { \
				(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
				(_fl_)->objlist = _p; \
				(_fl_)->free_cnt++; \
			} \
		} \
		else { \
			break; \
		} \
	} \
	NASD_FREELIST_STAT_FREE_UPDATE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 * init  = init obj func
 */
#define NASD_FREELIST_GET_INIT(_fl_,_obj_,_nextp_,_cast_,_init_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	if (_fl_->objlist) { \
		_obj_ = _cast_((_fl_)->objlist); \
		(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
		(_fl_)->free_cnt--; \
	} \
	else { \
		/* \
		 * Allocate one at a time so we can free \
		 * one at a time without cleverness when arena \
		 * is full. \
		 */ \
		NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
		if (_obj_) { \
			if (_init_ (_obj_)) { \
				NASD_Free(_obj_,(_fl_)->obj_size); \
				_obj_ = NULL; \
			} \
			else { \
				for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
					NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
					if (_p) { \
						if (_init_ (_p)) { \
							NASD_Free(_p,(_fl_)->obj_size); \
							_p = NULL; \
							break; \
						} \
						(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
						(_fl_)->objlist = _p; \
					} \
					else { \
						break; \
					} \
				} \
			} \
		} \
		NASD_FREELIST_STAT_GROW(_fl_); \
	} \
	NASD_FREELIST_STAT_ALLOC(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 * init  = init obj func
 * arg   = arg to init obj func
 */
#define NASD_FREELIST_GET_INIT_ARG(_fl_,_obj_,_nextp_,_cast_,_init_,_arg_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	if (_fl_->objlist) { \
		_obj_ = _cast_((_fl_)->objlist); \
		(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
		(_fl_)->free_cnt--; \
	} \
	else { \
		/* \
		 * Allocate one at a time so we can free \
		 * one at a time without cleverness when arena \
		 * is full. \
		 */ \
		NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
		if (_obj_) { \
			if (_init_ (_obj_,_arg_)) { \
				NASD_Free(_obj_,(_fl_)->obj_size); \
				_obj_ = NULL; \
			} \
			else { \
				for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
					NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
					if (_p) { \
						if (_init_ (_p,_arg_)) { \
							NASD_Free(_p,(_fl_)->obj_size); \
							_p = NULL; \
							break; \
						} \
						(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
						(_fl_)->objlist = _p; \
					} \
					else { \
						break; \
					} \
				} \
			} \
		} \
		NASD_FREELIST_STAT_GROW(_fl_); \
	} \
	NASD_FREELIST_STAT_ALLOC(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 * init  = init obj func
 */
#define NASD_FREELIST_GET_INIT_NOUNLOCK(_fl_,_obj_,_nextp_,_cast_,_init_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	if (_fl_->objlist) { \
		_obj_ = _cast_((_fl_)->objlist); \
		(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
		(_fl_)->free_cnt--; \
	} \
	else { \
		/* \
		 * Allocate one at a time so we can free \
		 * one at a time without cleverness when arena \
		 * is full. \
		 */ \
		NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
		if (_obj_) { \
			if (_init_ (_obj_)) { \
				NASD_Free(_obj_,(_fl_)->obj_size); \
				_obj_ = NULL; \
			} \
			else { \
				for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
					NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
					if (_p) { \
						if (_init_ (_p)) { \
							NASD_Free(_p,(_fl_)->obj_size); \
							_p = NULL; \
							break; \
						} \
						(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
						(_fl_)->objlist = _p; \
					} \
					else { \
						break; \
					} \
				} \
			} \
		} \
		NASD_FREELIST_STAT_GROW(_fl_); \
	} \
	NASD_FREELIST_STAT_ALLOC(_fl_); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 */
#define NASD_FREELIST_GET(_fl_,_obj_,_nextp_,_cast_) { \
	void *_p; \
	int _i; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	if (_fl_->objlist) { \
		_obj_ = _cast_((_fl_)->objlist); \
		(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
		(_fl_)->free_cnt--; \
	} \
	else { \
		/* \
		 * Allocate one at a time so we can free \
		 * one at a time without cleverness when arena \
		 * is full. \
		 */ \
		NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
		if (_obj_) { \
			for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
				NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
				if (_p) { \
					(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
					(_fl_)->objlist = _p; \
				} \
				else { \
					break; \
				} \
			} \
		} \
		NASD_FREELIST_STAT_GROW(_fl_); \
	} \
	NASD_FREELIST_STAT_ALLOC(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 */
#define NASD_FREELIST_GET_NOLOCKING(_fl_,_obj_,_nextp_,_cast_) { \
	void *_p; \
	int _i; \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	if (_fl_->objlist) { \
		_obj_ = _cast_((_fl_)->objlist); \
		(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
		(_fl_)->free_cnt--; \
	} \
	else { \
		/* \
		 * Allocate one at a time so we can free \
		 * one at a time without cleverness when arena \
		 * is full. \
		 */ \
		NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
		if (_obj_) { \
			for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
				NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
				if (_p) { \
					(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
					(_fl_)->objlist = _p; \
				} \
				else { \
					break; \
				} \
			} \
		} \
		NASD_FREELIST_STAT_GROW(_fl_); \
	} \
	NASD_FREELIST_STAT_ALLOC(_fl_); \
}

/*
 * fl    = freelist
 * obj   = object to allocate
 * nextp = name of "next" pointer in obj
 * cast  = cast of obj assignment
 * num   = num objs to return
 */
#define NASD_FREELIST_GET_N(_fl_,_obj_,_nextp_,_cast_,_num_) { \
	void *_p, *_l, *_f; \
	int _i, _n; \
	_l = _f = NULL; \
	_n = 0; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	NASD_ASSERT(sizeof(*(_obj_))==((_fl_)->obj_size)); \
	for(_n=0;_n<_num_;_n++) { \
		if (_fl_->objlist) { \
			_obj_ = _cast_((_fl_)->objlist); \
			(_fl_)->objlist = (void *)((_obj_)->_nextp_); \
			(_fl_)->free_cnt--; \
		} \
		else { \
			/* \
			 * Allocate one at a time so we can free \
			 * one at a time without cleverness when arena \
			 * is full. \
			 */ \
			NASD_Malloc(_obj_,(_fl_)->obj_size,_cast_); \
			if (_obj_) { \
				for(_i=1;_i<(_fl_)->obj_inc;_i++) { \
					NASD_Malloc(_p,(_fl_)->obj_size,(void *)); \
					if (_p) { \
						(_cast_(_p))->_nextp_ = (_fl_)->objlist; \
						(_fl_)->objlist = _p; \
					} \
					else { \
						break; \
					} \
				} \
			} \
			NASD_FREELIST_STAT_GROW(_fl_); \
		} \
		if (_f == NULL) \
			_f = _obj_; \
		if (_obj_) { \
			(_cast_(_obj_))->_nextp_ = _l; \
			_l = _obj_; \
			NASD_FREELIST_STAT_ALLOC(_fl_); \
		} \
		else { \
			(_cast_(_f))->_nextp_ = (_fl_)->objlist; \
			(_fl_)->objlist = _l; \
			_n = _num_; \
		} \
	} \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 */
#define NASD_FREELIST_FREE(_fl_,_obj_,_nextp_) { \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
		NASD_Free(_obj_,(_fl_)->obj_size); \
	} \
	else { \
		NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
		(_obj_)->_nextp_ = (_fl_)->objlist; \
		(_fl_)->objlist = (void *)(_obj_); \
		(_fl_)->free_cnt++; \
	} \
	NASD_FREELIST_STAT_FREE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 */
#define NASD_FREELIST_FREE_NOLOCKING(_fl_,_obj_,_nextp_) { \
	if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
		NASD_Free(_obj_,(_fl_)->obj_size); \
	} \
	else { \
		NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
		(_obj_)->_nextp_ = (_fl_)->objlist; \
		(_fl_)->objlist = (void *)(_obj_); \
		(_fl_)->free_cnt++; \
	} \
	NASD_FREELIST_STAT_FREE(_fl_); \
}

/*
 * fl    = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 * num   = num to free (debugging)
 */
#define NASD_FREELIST_FREE_N(_fl_,_obj_,_nextp_,_cast_,_num_) { \
	void *_no; \
	int _n; \
	_n = 0; \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	while(_obj_) { \
		_no = (_cast_(_obj_))->_nextp_; \
		if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
			NASD_Free(_obj_,(_fl_)->obj_size); \
		} \
		else { \
			NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
			(_obj_)->_nextp_ = (_fl_)->objlist; \
			(_fl_)->objlist = (void *)(_obj_); \
			(_fl_)->free_cnt++; \
		} \
		_n++; \
		_obj_ = _no; \
		NASD_FREELIST_STAT_FREE(_fl_); \
	} \
	NASD_ASSERT(_n==(_num_)); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 * clean = undo for init
 */
#define NASD_FREELIST_FREE_CLEAN(_fl_,_obj_,_nextp_,_clean_) { \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
		_clean_ (_obj_); \
		NASD_Free(_obj_,(_fl_)->obj_size); \
	} \
	else { \
		NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
		(_obj_)->_nextp_ = (_fl_)->objlist; \
		(_fl_)->objlist = (void *)(_obj_); \
		(_fl_)->free_cnt++; \
	} \
	NASD_FREELIST_STAT_FREE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 * clean = undo for init
 * arg   = arg for undo func
 */
#define NASD_FREELIST_FREE_CLEAN_ARG(_fl_,_obj_,_nextp_,_clean_,_arg_) { \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
		_clean_ (_obj_,_arg_); \
		NASD_Free(_obj_,(_fl_)->obj_size); \
	} \
	else { \
		NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
		(_obj_)->_nextp_ = (_fl_)->objlist; \
		(_fl_)->objlist = (void *)(_obj_); \
		(_fl_)->free_cnt++; \
	} \
	NASD_FREELIST_STAT_FREE(_fl_); \
	NASD_UNLOCK_MUTEX((_fl_)->lock); \
}

/*
 * fl    = freelist
 * obj   = object to free
 * nextp = name of "next" pointer in obj
 * clean = undo for init
 */
#define NASD_FREELIST_FREE_CLEAN_NOUNLOCK(_fl_,_obj_,_nextp_,_clean_) { \
	NASD_LOCK_MUTEX((_fl_)->lock); \
	if ((_fl_)->free_cnt == (_fl_)->max_free_cnt) { \
		_clean_ (_obj_); \
		NASD_Free(_obj_,(_fl_)->obj_size); \
	} \
	else { \
		NASD_ASSERT((_fl_)->free_cnt < (_fl_)->max_free_cnt); \
		(_obj_)->_nextp_ = (_fl_)->objlist; \
		(_fl_)->objlist = (void *)(_obj_); \
		(_fl_)->free_cnt++; \
	} \
	NASD_FREELIST_STAT_FREE(_fl_); \
}

/*
 * fl     = freelist
 * nextp = name of "next" pointer in obj
 * cast  = cast to object type
 */
#define NASD_FREELIST_DESTROY(_fl_,_nextp_,_cast_) { \
	void *_cur, *_next; \
	NASD_FREELIST_STAT_REPORT(_fl_); \
	nasd_mutex_destroy(&((_fl_)->lock)); \
	for(_cur=(_fl_)->objlist;_cur;_cur=_next) { \
		_next = (_cast_ _cur)->_nextp_; \
		NASD_Free(_cur,(_fl_)->obj_size); \
	} \
	NASD_Free(_fl_,sizeof(nasd_freelist_t)); \
    _fl_ = NULL; \
}

#define NASD_FENCEPOST_FREELIST(_fl_,_nextp_,_cast_) { \
	void *_cur, *_next; \
    NASD_Fencepost(_fl_,sizeof(nasd_freelist_t),1); \
	for(_cur=(_fl_)->objlist;_cur;_cur=_next) { \
		_next = (_cast_ _cur)->_nextp_; \
        NASD_Fencepost(_cur,(_fl_)->obj_size,2); \
	} \
    NASD_Fencepost(_fl_,sizeof(nasd_freelist_t),3); \
}

/*
 * fl    = freelist
 * nextp = name of "next" pointer in obj
 * cast  = cast to object type
 * clean = func to undo obj init
 */
#define NASD_FREELIST_DESTROY_CLEAN(_fl_,_nextp_,_cast_,_clean_) { \
	void *_cur, *_next; \
	NASD_FREELIST_STAT_REPORT(_fl_); \
	nasd_mutex_destroy(&((_fl_)->lock)); \
	for(_cur=(_fl_)->objlist;_cur;_cur=_next) { \
		_next = (_cast_ _cur)->_nextp_; \
		_clean_ (_cur); \
        (_fl_)->objlist = _next; \
		NASD_Free(_cur,(_fl_)->obj_size); \
	} \
	NASD_Free(_fl_,sizeof(nasd_freelist_t)); \
    _fl_ = NULL; \
}

/*
 * fl    = freelist
 * nextp = name of "next" pointer in obj
 * cast  = cast to object type
 * clean = func to undo obj init
 * arg   = arg for undo func
 */
#define NASD_FREELIST_DESTROY_CLEAN_ARG(_fl_,_nextp_,_cast_,_clean_,_arg_) { \
	void *_cur, *_next; \
	NASD_FREELIST_STAT_REPORT(_fl_); \
	nasd_mutex_destroy(&((_fl_)->lock)); \
	for(_cur=(_fl_)->objlist;_cur;_cur=_next) { \
		_next = (_cast_ _cur)->_nextp_; \
		_clean_ (_cur,_arg_); \
		NASD_Free(_cur,(_fl_)->obj_size); \
	} \
	NASD_Free(_fl_,sizeof(nasd_freelist_t)); \
    _fl_ = NULL; \
}

#endif /* !_NASD_FREELIST_H_ */
