/*
 * Copyright (C) 2001  Internet Software Consortium.
 *
 * Permission to use, copy, modify, and 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 INTERNET SOFTWARE CONSORTIUM
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
 * INTERNET SOFTWARE CONSORTIUM 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: refcount.h,v 1.1.2.2 2001/02/09 00:27:50 gson Exp $ */

#ifndef ISC_REFCOUNT_H
#define ISC_REFCOUNT_H 1

#include <isc/lang.h>
#include <isc/mutex.h>
#include <isc/platform.h>
#include <isc/types.h>
#include <isc/util.h>

/*
 * Implements a locked reference counter.  These functions may actually be
 * implemented using macros, and implementations of these macros are below.
 * The isc_refcount_t type should not be accessed directly, as its contents
 * depend on the implementation.
 */

ISC_LANG_BEGINDECLS

/*
 * Function prototypes
 */

/*
 * void
 * isc_refcount_init(isc_refcount_t *ref, unsigned int n);
 *
 * Initialize the reference counter.  There will be 'n' initial references.
 *
 * Requires:
 *	ref != NULL
 */

/*
 * void
 * isc_refcount_destroy(isc_refcount_t *ref);
 *
 * Destroys a reference counter.
 *
 * Requires:
 *	ref != NULL
 *	The number of references is 0.
 */

/*
 * void
 * isc_refcount_increment(isc_refcount_t *ref, unsigned int *targetp);
 *
 * Increments the reference count, returning the new value in targetp if it's
 * not NULL.
 *
 * Requires:
 *	ref != NULL.
 */

/*
 * void
 * isc_refcount_decrement(isc_refcount_t *ref, unsigned int *targetp);
 *
 * Decrements the reference count,  returning the new value in targetp if it's
 * not NULL.
 *
 * Requires:
 *	ref != NULL.
 */


/*
 * Sample implementations
 */
#ifdef ISC_PLATFORM_USETHREADS

typedef struct isc_refcount {
	int refs;
	isc_mutex_t lock;
} isc_refcount_t;

#define isc_refcount_init(rp, n) 			\
	do {						\
		isc_result_t _r;			\
		(rp)->refs = (n);			\
		_r = isc_mutex_init(&(rp)->lock);	\
		RUNTIME_CHECK(_r == ISC_R_SUCCESS);	\
	} while (0)

#define isc_refcount_destroy(rp)			\
	do {						\
		REQUIRE((rp)->refs == 0);		\
		DESTROYLOCK(&(rp)->lock);		\
	} while (0)

#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))

#define isc_refcount_increment(rp, tp)				\
	do {							\
		LOCK(&(rp)->lock);				\
		REQUIRE((rp)->refs > 0);			\
		++((rp)->refs);					\
		if ((tp) != NULL)				\
			*(unsigned int *)(tp) = ((rp)->refs);	\
		UNLOCK(&(rp)->lock);				\
	} while (0)

#define isc_refcount_decrement(rp, tp)				\
	do {							\
		LOCK(&(rp)->lock);				\
		REQUIRE((rp)->refs > 0);			\
		--((rp)->refs);					\
		if ((tp) != NULL)				\
			*(unsigned int *)(tp) = ((rp)->refs);	\
		UNLOCK(&(rp)->lock);				\
	} while (0)

#else

typedef struct isc_refcount {
	int refs;
} isc_refcount_t;

#define isc_refcount_init(rp, n) ((rp)->refs = (n))
#define isc_refcount_destroy(rp) (REQUIRE((rp)->refs == 0))
#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))

#define isc_refcount_increment(rp, tp)					\
	do {								\
		int _n = ++(rp)->refs;					\
		if ((tp) != NULL)					\
			*(unsigned int *)(tp) = (unsigned int)(_n);	\
	} while (0)

#define isc_refcount_decrement(rp, tp)					\
	do {								\
		int _n = --(rp)->refs;					\
		if ((tp) != NULL)					\
			*(unsigned int *)(tp) = (unsigned int)(_n);	\
	} while (0)

#endif

ISC_LANG_ENDDECLS

#endif /* ISC_REFCOUNT_H */
