refcount.h revision 290001
1/*
2 * Copyright (C) 2004-2007, 2009  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2001, 2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: refcount.h,v 1.17 2009/09/29 23:48:04 tbox Exp $ */
19
20#ifndef ISC_REFCOUNT_H
21#define ISC_REFCOUNT_H 1
22
23#include <isc/atomic.h>
24#include <isc/lang.h>
25#include <isc/mutex.h>
26#include <isc/platform.h>
27#include <isc/types.h>
28#include <isc/util.h>
29
30/*! \file isc/refcount.h
31 * \brief Implements a locked reference counter.
32 *
33 * These functions may actually be
34 * implemented using macros, and implementations of these macros are below.
35 * The isc_refcount_t type should not be accessed directly, as its contents
36 * depend on the implementation.
37 */
38
39ISC_LANG_BEGINDECLS
40
41/*
42 * Function prototypes
43 */
44
45/*
46 * isc_result_t
47 * isc_refcount_init(isc_refcount_t *ref, unsigned int n);
48 *
49 * Initialize the reference counter.  There will be 'n' initial references.
50 *
51 * Requires:
52 *	ref != NULL
53 */
54
55/*
56 * void
57 * isc_refcount_destroy(isc_refcount_t *ref);
58 *
59 * Destroys a reference counter.
60 *
61 * Requires:
62 *	ref != NULL
63 *	The number of references is 0.
64 */
65
66/*
67 * void
68 * isc_refcount_increment(isc_refcount_t *ref, unsigned int *targetp);
69 * isc_refcount_increment0(isc_refcount_t *ref, unsigned int *targetp);
70 *
71 * Increments the reference count, returning the new value in targetp if it's
72 * not NULL.  The reference counter typically begins with the initial counter
73 * of 1, and will be destroyed once the counter reaches 0.  Thus,
74 * isc_refcount_increment() additionally requires the previous counter be
75 * larger than 0 so that an error which violates the usage can be easily
76 * caught.  isc_refcount_increment0() does not have this restriction.
77 *
78 * Requires:
79 *	ref != NULL.
80 */
81
82/*
83 * void
84 * isc_refcount_decrement(isc_refcount_t *ref, unsigned int *targetp);
85 *
86 * Decrements the reference count,  returning the new value in targetp if it's
87 * not NULL.
88 *
89 * Requires:
90 *	ref != NULL.
91 */
92
93
94/*
95 * Sample implementations
96 */
97#ifdef ISC_PLATFORM_USETHREADS
98#ifdef ISC_PLATFORM_HAVEXADD
99
100#define ISC_REFCOUNT_HAVEATOMIC 1
101
102typedef struct isc_refcount {
103	isc_int32_t refs;
104} isc_refcount_t;
105
106#define isc_refcount_destroy(rp) REQUIRE((rp)->refs == 0)
107#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))
108
109#define isc_refcount_increment0(rp, tp)				\
110	do {							\
111		unsigned int *_tmp = (unsigned int *)(tp);	\
112		isc_int32_t prev;				\
113		prev = isc_atomic_xadd(&(rp)->refs, 1);		\
114		if (_tmp != NULL)				\
115			*_tmp = prev + 1;			\
116	} while (0)
117
118#define isc_refcount_increment(rp, tp)				\
119	do {							\
120		unsigned int *_tmp = (unsigned int *)(tp);	\
121		isc_int32_t prev;				\
122		prev = isc_atomic_xadd(&(rp)->refs, 1);		\
123		REQUIRE(prev > 0);				\
124		if (_tmp != NULL)				\
125			*_tmp = prev + 1;			\
126	} while (0)
127
128#define isc_refcount_decrement(rp, tp)				\
129	do {							\
130		unsigned int *_tmp = (unsigned int *)(tp);	\
131		isc_int32_t prev;				\
132		prev = isc_atomic_xadd(&(rp)->refs, -1);	\
133		REQUIRE(prev > 0);				\
134		if (_tmp != NULL)				\
135			*_tmp = prev - 1;			\
136	} while (0)
137
138#else  /* ISC_PLATFORM_HAVEXADD */
139
140typedef struct isc_refcount {
141	int refs;
142	isc_mutex_t lock;
143} isc_refcount_t;
144
145/*% Destroys a reference counter. */
146#define isc_refcount_destroy(rp)			\
147	do {						\
148		REQUIRE((rp)->refs == 0);		\
149		DESTROYLOCK(&(rp)->lock);		\
150	} while (0)
151
152#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))
153
154/*% Increments the reference count, returning the new value in targetp if it's not NULL. */
155#define isc_refcount_increment0(rp, tp)				\
156	do {							\
157		unsigned int *_tmp = (unsigned int *)(tp);	\
158		LOCK(&(rp)->lock);				\
159		++((rp)->refs);					\
160		if (_tmp != NULL)				\
161			*_tmp = ((rp)->refs);			\
162		UNLOCK(&(rp)->lock);				\
163	} while (0)
164
165#define isc_refcount_increment(rp, tp)				\
166	do {							\
167		unsigned int *_tmp = (unsigned int *)(tp);	\
168		LOCK(&(rp)->lock);				\
169		REQUIRE((rp)->refs > 0);			\
170		++((rp)->refs);					\
171		if (_tmp != NULL)				\
172			*_tmp = ((rp)->refs);			\
173		UNLOCK(&(rp)->lock);				\
174	} while (0)
175
176/*% Decrements the reference count,  returning the new value in targetp if it's not NULL. */
177#define isc_refcount_decrement(rp, tp)				\
178	do {							\
179		unsigned int *_tmp = (unsigned int *)(tp);	\
180		LOCK(&(rp)->lock);				\
181		REQUIRE((rp)->refs > 0);			\
182		--((rp)->refs);					\
183		if (_tmp != NULL)				\
184			*_tmp = ((rp)->refs);			\
185		UNLOCK(&(rp)->lock);				\
186	} while (0)
187
188#endif /* ISC_PLATFORM_HAVEXADD */
189#else  /* ISC_PLATFORM_USETHREADS */
190
191typedef struct isc_refcount {
192	int refs;
193} isc_refcount_t;
194
195#define isc_refcount_destroy(rp) REQUIRE((rp)->refs == 0)
196#define isc_refcount_current(rp) ((unsigned int)((rp)->refs))
197
198#define isc_refcount_increment0(rp, tp)					\
199	do {								\
200		unsigned int *_tmp = (unsigned int *)(tp);		\
201		int _n = ++(rp)->refs;					\
202		if (_tmp != NULL)					\
203			*_tmp = _n;					\
204	} while (0)
205
206#define isc_refcount_increment(rp, tp)					\
207	do {								\
208		unsigned int *_tmp = (unsigned int *)(tp);		\
209		int _n;							\
210		REQUIRE((rp)->refs > 0);				\
211		_n = ++(rp)->refs;					\
212		if (_tmp != NULL)					\
213			*_tmp = _n;					\
214	} while (0)
215
216#define isc_refcount_decrement(rp, tp)					\
217	do {								\
218		unsigned int *_tmp = (unsigned int *)(tp);		\
219		int _n;							\
220		REQUIRE((rp)->refs > 0);				\
221		_n = --(rp)->refs;					\
222		if (_tmp != NULL)					\
223			*_tmp = _n;					\
224	} while (0)
225
226#endif /* ISC_PLATFORM_USETHREADS */
227
228isc_result_t
229isc_refcount_init(isc_refcount_t *ref, unsigned int n);
230
231ISC_LANG_ENDDECLS
232
233#endif /* ISC_REFCOUNT_H */
234