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 
39 ISC_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 
102 typedef 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 
140 typedef 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 
191 typedef 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 
228 isc_result_t
229 isc_refcount_init(isc_refcount_t *ref, unsigned int n);
230 
231 ISC_LANG_ENDDECLS
232 
233 #endif /* ISC_REFCOUNT_H */
234