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