1 /*
2 * Copyright (c) 2015 Sippy Software, Inc., http://www.sippysoft.com
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28 #ifndef HAVE_GCC_ATOMICS
29 #include <pthread.h>
30 #endif
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "config.h"
36 #include "rtpp_debug.h"
37 #include "rtpp_types.h"
38 #include "rtpp_mallocs.h"
39 #include "rtpp_refcnt.h"
40 #include "rtpp_refcnt_fin.h"
41
42 #if RTPP_DEBUG_refcnt
43 #include <stdio.h>
44 #ifdef RTPP_DEBUG
45 #include "rtpp_stacktrace.h"
46 #endif
47 #endif
48
49 /*
50 * Somewhat arbitrary cap on the maximum value of the references. Just here
51 * to catch any runaway situations, i.e. bugs in the code.
52 */
53 #define RC_ABS_MAX 2000000
54
55 #define RC_FLAG_PA (1 << 0)
56 #define RC_FLAG_TRACE (1 << 1)
57
58 struct rtpp_refcnt_priv
59 {
60 struct rtpp_refcnt pub;
61 volatile int32_t cnt;
62 #ifndef HAVE_GCC_ATOMICS
63 pthread_mutex_t cnt_lock;
64 #endif
65 rtpp_refcnt_dtor_t dtor_f;
66 void *data;
67 rtpp_refcnt_dtor_t pre_dtor_f;
68 void *pd_data;
69 int flags;
70 };
71
72 static void rtpp_refcnt_attach(struct rtpp_refcnt *, rtpp_refcnt_dtor_t,
73 void *);
74 static void rtpp_refcnt_incref(struct rtpp_refcnt *);
75 static void rtpp_refcnt_decref(struct rtpp_refcnt *);
76 static void *rtpp_refcnt_getdata(struct rtpp_refcnt *);
77 static void rtpp_refcnt_reg_pd(struct rtpp_refcnt *, rtpp_refcnt_dtor_t,
78 void *);
79 #if RTPP_DEBUG_refcnt
80 static void rtpp_refcnt_traceen(struct rtpp_refcnt *);
81 #endif
82
83 const struct rtpp_refcnt_smethods rtpp_refcnt_smethods = {
84 .incref = &rtpp_refcnt_incref,
85 .decref = &rtpp_refcnt_decref,
86 .getdata = &rtpp_refcnt_getdata,
87 .reg_pd = &rtpp_refcnt_reg_pd,
88 #if RTPP_DEBUG_refcnt
89 .traceen = rtpp_refcnt_traceen,
90 #endif
91 .attach = &rtpp_refcnt_attach
92 };
93
94 struct rtpp_refcnt *
rtpp_refcnt_ctor(void * data,rtpp_refcnt_dtor_t dtor_f)95 rtpp_refcnt_ctor(void *data, rtpp_refcnt_dtor_t dtor_f)
96 {
97 struct rtpp_refcnt_priv *pvt;
98
99 pvt = rtpp_zmalloc(sizeof(struct rtpp_refcnt_priv));
100 if (pvt == NULL) {
101 return (NULL);
102 }
103 #ifndef HAVE_GCC_ATOMICS
104 if (pthread_mutex_init(&pvt->cnt_lock, NULL) != 0) {
105 free(pvt);
106 return (NULL);
107 }
108 #endif
109 pvt->data = data;
110 if (dtor_f != NULL) {
111 pvt->dtor_f = dtor_f;
112 } else {
113 pvt->dtor_f = free;
114 }
115 pvt->pub.smethods = &rtpp_refcnt_smethods;
116 pvt->cnt = 1;
117 return (&pvt->pub);
118 }
119
120 const unsigned int
rtpp_refcnt_osize(void)121 rtpp_refcnt_osize(void)
122 {
123
124 return (sizeof(struct rtpp_refcnt_priv));
125 }
126
127 struct rtpp_refcnt *
rtpp_refcnt_ctor_pa(void * pap)128 rtpp_refcnt_ctor_pa(void *pap)
129 {
130 struct rtpp_refcnt_priv *pvt;
131
132 pvt = (struct rtpp_refcnt_priv *)pap;
133 #ifndef HAVE_GCC_ATOMICS
134 if (pthread_mutex_init(&pvt->cnt_lock, NULL) != 0) {
135 return (NULL);
136 }
137 #endif
138 pvt->pub.smethods = &rtpp_refcnt_smethods;
139 pvt->cnt = 1;
140 pvt->flags |= RC_FLAG_PA;
141 return (&pvt->pub);
142 }
143
144 static void
rtpp_refcnt_attach(struct rtpp_refcnt * pub,rtpp_refcnt_dtor_t dtor_f,void * data)145 rtpp_refcnt_attach(struct rtpp_refcnt *pub, rtpp_refcnt_dtor_t dtor_f,
146 void *data)
147 {
148 struct rtpp_refcnt_priv *pvt;
149
150 pvt = (struct rtpp_refcnt_priv *)pub;
151 pvt->data = data;
152 pvt->dtor_f = dtor_f;
153 }
154
155 static void
rtpp_refcnt_incref(struct rtpp_refcnt * pub)156 rtpp_refcnt_incref(struct rtpp_refcnt *pub)
157 {
158 struct rtpp_refcnt_priv *pvt;
159
160 pvt = (struct rtpp_refcnt_priv *)pub;
161 #ifndef HAVE_GCC_ATOMICS
162 pthread_mutex_lock(&pvt->cnt_lock);
163 #endif
164 #if RTPP_DEBUG_refcnt
165 if (pvt->flags & RC_FLAG_TRACE) {
166 char *dbuf;
167 asprintf(&dbuf, "rtpp_refcnt(%p, %u).incref()", pub, pvt->cnt);
168 if (dbuf != NULL) {
169 #ifdef RTPP_DEBUG
170 rtpp_stacktrace_print(dbuf);
171 #else
172 fprintf(stderr, "%s\n", dbuf);
173 #endif
174 free(dbuf);
175 }
176 }
177 #endif
178 RTPP_DBG_ASSERT(pvt->cnt > 0 && pvt->cnt < RC_ABS_MAX);
179 #ifndef HAVE_GCC_ATOMICS
180 pthread_mutex_unlock(&pvt->cnt_lock);
181 pvt->cnt += 1;
182 #else
183 __sync_fetch_and_add(&pvt->cnt, 1);
184 #endif
185 }
186
187 static void
rtpp_refcnt_decref(struct rtpp_refcnt * pub)188 rtpp_refcnt_decref(struct rtpp_refcnt *pub)
189 {
190 struct rtpp_refcnt_priv *pvt;
191 int oldcnt;
192
193 pvt = (struct rtpp_refcnt_priv *)pub;
194 #ifndef HAVE_GCC_ATOMICS
195 pthread_mutex_lock(&pvt->cnt_lock);
196 oldcnt = pvt->cnt;
197 pvt->cnt -= 1;
198 #else
199 oldcnt = __sync_fetch_and_add(&pvt->cnt, -1);
200 #endif
201 #if RTPP_DEBUG_refcnt
202 if (pvt->flags & RC_FLAG_TRACE) {
203 char *dbuf;
204 asprintf(&dbuf, "rtpp_refcnt(%p, %u).decref()", pub, oldcnt);
205 if (dbuf != NULL) {
206 #ifdef RTPP_DEBUG
207 rtpp_stacktrace_print(dbuf);
208 #else
209 fprintf(stderr, "%s\n", dbuf);
210 #endif
211 free(dbuf);
212 }
213 }
214 #endif
215 if (oldcnt == 1) {
216 if ((pvt->flags & RC_FLAG_PA) == 0) {
217 if (pvt->pre_dtor_f != NULL) {
218 pvt->pre_dtor_f(pvt->pd_data);
219 }
220 pvt->dtor_f(pvt->data);
221 rtpp_refcnt_fin(pub);
222 #ifndef HAVE_GCC_ATOMICS
223 pthread_mutex_unlock(&pvt->cnt_lock);
224 pthread_mutex_destroy(&pvt->cnt_lock);
225 #endif
226 free(pvt);
227 } else {
228 #ifndef HAVE_GCC_ATOMICS
229 pthread_mutex_unlock(&pvt->cnt_lock);
230 pthread_mutex_destroy(&pvt->cnt_lock);
231 #endif
232 rtpp_refcnt_fin(pub);
233 if (pvt->pre_dtor_f != NULL) {
234 pvt->pre_dtor_f(pvt->pd_data);
235 }
236 if (pvt->dtor_f != NULL) {
237 pvt->dtor_f(pvt->data);
238 }
239 }
240
241 return;
242 }
243 #ifndef HAVE_GCC_ATOMICS
244 RTPP_DBG_ASSERT(pvt->cnt > 0);
245 pthread_mutex_unlock(&pvt->cnt_lock);
246 #endif
247 }
248
249 #if 0
250 /*
251 * Special case destructor, only when we want to abort object without
252 * calling any registered callbacks, i.e. when rolling back failed
253 * constructor in the complex class.
254 */
255 static void
256 rtpp_refcnt_abort(struct rtpp_refcnt *pub)
257 {
258 struct rtpp_refcnt_priv *pvt;
259
260 pvt = (struct rtpp_refcnt_priv *)pub;
261 #ifndef HAVE_GCC_ATOMICS
262 pthread_mutex_lock(&pvt->cnt_lock);
263 #endif
264 RTPP_DBG_ASSERT(pvt->cnt == 1);
265 #ifndef HAVE_GCC_ATOMICS
266 pthread_mutex_unlock(&pvt->cnt_lock);
267 pthread_mutex_destroy(&pvt->cnt_lock);
268 #endif
269 if ((pvt->flags & RC_FLAG_PA) == 0) {
270 free(pvt);
271 }
272 return;
273 }
274 #endif
275
276 static void *
rtpp_refcnt_getdata(struct rtpp_refcnt * pub)277 rtpp_refcnt_getdata(struct rtpp_refcnt *pub)
278 {
279 struct rtpp_refcnt_priv *pvt;
280
281 pvt = (struct rtpp_refcnt_priv *)pub;
282 RTPP_DBG_ASSERT(pvt->cnt > 0);
283 return (pvt->data);
284 }
285
286 static void
rtpp_refcnt_reg_pd(struct rtpp_refcnt * pub,rtpp_refcnt_dtor_t pre_dtor_f,void * pd_data)287 rtpp_refcnt_reg_pd(struct rtpp_refcnt *pub, rtpp_refcnt_dtor_t pre_dtor_f,
288 void *pd_data)
289 {
290 struct rtpp_refcnt_priv *pvt;
291
292 pvt = (struct rtpp_refcnt_priv *)pub;
293 RTPP_DBG_ASSERT(pvt->pre_dtor_f == NULL);
294 pvt->pre_dtor_f = pre_dtor_f;
295 pvt->pd_data = pd_data;
296 }
297
298 #if RTPP_DEBUG_refcnt
299 static void
rtpp_refcnt_traceen(struct rtpp_refcnt * pub)300 rtpp_refcnt_traceen(struct rtpp_refcnt *pub)
301 {
302 struct rtpp_refcnt_priv *pvt;
303
304 pvt = (struct rtpp_refcnt_priv *)pub;
305 pvt->flags |= RC_FLAG_TRACE;
306 }
307 #endif
308