/src/rtpproxy/src/rtpp_refcnt.c
Line | Count | Source |
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 | | #if defined(LINUX_XXX) && !defined(_GNU_SOURCE) |
29 | | /* Apparently needed for asprintf(3) */ |
30 | | #define _GNU_SOURCE |
31 | | #endif |
32 | | |
33 | | #include <dlfcn.h> |
34 | | #include <stdatomic.h> |
35 | | #include <stddef.h> |
36 | | #include <stdio.h> |
37 | | #include <stdint.h> |
38 | | #include <stdlib.h> |
39 | | #include <string.h> |
40 | | |
41 | | #include "config.h" |
42 | | #include "rtpp_debug.h" |
43 | | #include "rtpp_types.h" |
44 | | #include "rtpp_mallocs.h" |
45 | | #include "rtpp_codeptr.h" |
46 | | #include "rtpp_refcnt.h" |
47 | | #include "rtpp_refcnt_fin.h" |
48 | | |
49 | | #if RTPP_DEBUG_refcnt |
50 | | #include <stdio.h> |
51 | | #ifdef RTPP_DEBUG |
52 | | #include "rtpp_stacktrace.h" |
53 | | #endif |
54 | | #endif |
55 | | |
56 | | static void rtpp_refcnt_incref(struct rtpp_refcnt *, HERETYPE); |
57 | | static int rtpp_refcnt_tryincref(struct rtpp_refcnt *) |
58 | | __attribute__((warn_unused_result)); |
59 | | static void rtpp_refcnt_decref(struct rtpp_refcnt *, HERETYPE); |
60 | | |
61 | | /* |
62 | | * Somewhat arbitrary cap on the maximum value of the references. Just here |
63 | | * to catch any runaway situations, i.e. bugs in the code. |
64 | | */ |
65 | | #define RC_ABS_MAX 2000000 |
66 | | |
67 | | struct dtor_pair { |
68 | | rtpp_refcnt_dtor_t f; |
69 | | union { |
70 | | void *data; |
71 | | struct rtpp_refcnt *rcnt; |
72 | | }; |
73 | | }; |
74 | | |
75 | | struct rtpp_refcnt_priv |
76 | | { |
77 | | struct rtpp_refcnt pub; |
78 | | _Atomic(int) cnt __attribute__((aligned(CACHELINE_SIZE))); |
79 | | _Atomic(int) ulen __attribute__((aligned(CACHELINE_SIZE))); |
80 | | struct { |
81 | | unsigned int shared:1; |
82 | | #if RTPP_DEBUG_refcnt |
83 | | unsigned int trace:1; |
84 | | #endif |
85 | | } __attribute__((aligned(CACHELINE_SIZE))); |
86 | | struct dtor_pair dtors[MAX_DTORS]; |
87 | | }; |
88 | | const size_t rtpp_refcnt_osize = sizeof(struct rtpp_refcnt_priv); |
89 | | const size_t rtpp_refcnt_oalign = _Alignof(struct rtpp_refcnt_priv); |
90 | | |
91 | | static int rtpp_refcnt_attach(struct rtpp_refcnt *, rtpp_refcnt_dtor_t, |
92 | | void *) __attribute__((warn_unused_result)); |
93 | | static void rtpp_refcnt_attach_nc(struct rtpp_refcnt *, rtpp_refcnt_dtor_t, |
94 | | void *); |
95 | | static int rtpp_refcnt_attach_rc(struct rtpp_refcnt *, struct rtpp_refcnt *) |
96 | | __attribute__((warn_unused_result)); |
97 | | static void rtpp_refcnt_attach_rc_nc(struct rtpp_refcnt *, struct rtpp_refcnt *); |
98 | | static void *rtpp_refcnt_getdata(struct rtpp_refcnt *); |
99 | | |
100 | | #if RTPP_DEBUG_refcnt |
101 | | static void rtpp_refcnt_traceen(struct rtpp_refcnt *, HERETYPE); |
102 | | static int rtpp_refcnt_peek(struct rtpp_refcnt *); |
103 | | #endif |
104 | | |
105 | | DEFINE_SMETHODS(rtpp_refcnt, |
106 | | .incref = &rtpp_refcnt_incref, |
107 | | .tryincref = &rtpp_refcnt_tryincref, |
108 | | .decref = &rtpp_refcnt_decref, |
109 | | .getdata = &rtpp_refcnt_getdata, |
110 | | #if RTPP_DEBUG_refcnt |
111 | | .traceen = rtpp_refcnt_traceen, |
112 | | .peek = rtpp_refcnt_peek, |
113 | | #endif |
114 | | .attach = &rtpp_refcnt_attach, |
115 | | .attach_nc = &rtpp_refcnt_attach_nc, |
116 | | .attach_rc = &rtpp_refcnt_attach_rc, |
117 | | .attach_rc_nc = &rtpp_refcnt_attach_rc_nc, |
118 | | ); |
119 | | |
120 | | #if defined(RTPP_CHECK_LEAKS) |
121 | | static void |
122 | | rtpp_refcnt_free(void *p) |
123 | | { |
124 | | |
125 | | free(p); |
126 | | } |
127 | | #endif |
128 | | |
129 | 4.67M | #define DTOR_PAIR_INIT(fn, fd) (struct dtor_pair){.f=(fn), .data=(fd)} |
130 | 2.63M | #define DTOR_RC_INIT(rc) (struct dtor_pair){.rcnt=(rc)} |
131 | | |
132 | | struct rtpp_refcnt * |
133 | | rtpp_refcnt_ctor(void *data, rtpp_refcnt_dtor_t dtor_f) |
134 | 0 | { |
135 | 0 | struct rtpp_refcnt_priv *pvt; |
136 | |
|
137 | 0 | pvt = rtpp_zmalloc(sizeof(struct rtpp_refcnt_priv)); |
138 | 0 | if (pvt == NULL) { |
139 | 0 | return (NULL); |
140 | 0 | } |
141 | 0 | #if !defined(RTPP_CHECK_LEAKS) |
142 | 0 | pvt->dtors[0] = DTOR_PAIR_INIT(free, pvt); |
143 | | #else |
144 | | pvt->dtors[0] = DTOR_PAIR_INIT(rtpp_refcnt_free, pvt); |
145 | | #endif |
146 | 0 | if (dtor_f != NULL) { |
147 | 0 | pvt->dtors[1] = DTOR_PAIR_INIT(dtor_f, data); |
148 | 0 | atomic_init(&pvt->ulen, 1); |
149 | 0 | } else if (data != NULL) { |
150 | 0 | #if !defined(RTPP_CHECK_LEAKS) |
151 | 0 | pvt->dtors[1] = DTOR_PAIR_INIT(free, data); |
152 | | #else |
153 | | pvt->dtors[1] = DTOR_PAIR_INIT(rtpp_refcnt_free, data); |
154 | | #endif |
155 | 0 | atomic_init(&pvt->ulen, 1); |
156 | 0 | } |
157 | | #if defined(RTPP_DEBUG) |
158 | | pvt->pub.smethods = rtpp_refcnt_smethods; |
159 | | #endif |
160 | 0 | return (&pvt->pub); |
161 | 0 | } |
162 | | |
163 | | struct rtpp_refcnt * |
164 | | rtpp_refcnt_ctor_pa(void *pap, void *data) |
165 | 2.77M | { |
166 | 2.77M | struct rtpp_refcnt_priv *pvt; |
167 | | |
168 | 2.77M | pvt = (struct rtpp_refcnt_priv *)pap; |
169 | 2.77M | if (data != NULL) { |
170 | 2.77M | #if !defined(RTPP_CHECK_LEAKS) |
171 | 2.77M | pvt->dtors[0] = DTOR_PAIR_INIT(free, data); |
172 | | #else |
173 | | pvt->dtors[0] = DTOR_PAIR_INIT(rtpp_refcnt_free, data); |
174 | | #endif |
175 | 18.4E | } else { |
176 | 18.4E | atomic_init(&pvt->ulen, -1); |
177 | 18.4E | } |
178 | | #if defined(RTPP_DEBUG) |
179 | | pvt->pub.smethods = rtpp_refcnt_smethods; |
180 | | #endif |
181 | 2.77M | return (&pvt->pub); |
182 | 2.77M | } |
183 | | |
184 | | static int |
185 | | rtpp_refcnt_attach(struct rtpp_refcnt *pub, rtpp_refcnt_dtor_t dtor_f, |
186 | | void *data) |
187 | 1.90M | { |
188 | 1.90M | struct rtpp_refcnt_priv *pvt; |
189 | | |
190 | 1.90M | PUB2PVT(pub, pvt); |
191 | 1.90M | int ulen = atomic_fetch_add_explicit(&pvt->ulen, 1, memory_order_relaxed) + 1; |
192 | 1.90M | if (ulen >= MAX_DTORS) { |
193 | 0 | atomic_fetch_sub_explicit(&pvt->ulen, 1, memory_order_relaxed); |
194 | 0 | return -1; |
195 | 0 | } |
196 | 1.90M | pvt->dtors[ulen] = DTOR_PAIR_INIT(dtor_f, data); |
197 | 1.90M | return 0; |
198 | 1.90M | } |
199 | | |
200 | | static void |
201 | | rtpp_refcnt_attach_nc(struct rtpp_refcnt *pub, rtpp_refcnt_dtor_t dtor_f, |
202 | | void *data) |
203 | 1.69M | { |
204 | 1.69M | MAYBE_UNUSED int r = rtpp_refcnt_attach(pub, dtor_f, data); |
205 | 1.69M | RTPP_DBG_ASSERT(r == 0); |
206 | 1.69M | } |
207 | | |
208 | | static int |
209 | | rtpp_refcnt_attach_rc(struct rtpp_refcnt *pub, struct rtpp_refcnt *other) |
210 | 2.63M | { |
211 | 2.63M | struct rtpp_refcnt_priv *pvt; |
212 | | |
213 | 2.63M | PUB2PVT(pub, pvt); |
214 | 2.63M | int ulen = atomic_fetch_add_explicit(&pvt->ulen, 1, memory_order_relaxed) + 1; |
215 | 2.63M | if (ulen >= MAX_DTORS) { |
216 | 0 | atomic_fetch_sub_explicit(&pvt->ulen, 1, memory_order_relaxed); |
217 | 0 | return -1; |
218 | 0 | } |
219 | 2.63M | pvt->dtors[ulen] = DTOR_RC_INIT(other); |
220 | 2.63M | return 0; |
221 | 2.63M | } |
222 | | |
223 | | |
224 | | static void |
225 | | rtpp_refcnt_attach_rc_nc(struct rtpp_refcnt *pub, struct rtpp_refcnt *other) |
226 | 2.63M | { |
227 | 2.63M | MAYBE_UNUSED int r = rtpp_refcnt_attach_rc(pub, other); |
228 | 2.63M | RTPP_DBG_ASSERT(r == 0); |
229 | 2.63M | } |
230 | | |
231 | | static void |
232 | | rtpp_refcnt_incref(struct rtpp_refcnt *pub, HERETYPE mlp) |
233 | 3.42M | { |
234 | 3.42M | struct rtpp_refcnt_priv *pvt; |
235 | 3.42M | MAYBE_UNUSED int oldcnt; |
236 | | |
237 | 3.42M | PUB2PVT(pub, pvt); |
238 | 3.42M | RTPP_DBGCODE() { |
239 | 0 | oldcnt = atomic_load_explicit(&pvt->cnt, memory_order_relaxed); |
240 | 0 | RTPP_DBG_ASSERT(oldcnt >= 0 && oldcnt < RC_ABS_MAX); |
241 | 0 | } |
242 | 3.42M | if (pvt->shared) { |
243 | 2.70M | oldcnt = atomic_fetch_add_explicit(&pvt->cnt, 1, memory_order_relaxed); |
244 | 2.70M | } else { |
245 | 715k | oldcnt = 0; |
246 | 715k | pvt->shared = 1; |
247 | 715k | atomic_store_explicit(&pvt->cnt, 1, memory_order_release); |
248 | 715k | } |
249 | 3.42M | #if RTPP_DEBUG_refcnt |
250 | 3.42M | if (pvt->trace == 1) { |
251 | | #ifdef RTPP_DEBUG |
252 | | char *dbuf; |
253 | | rtpp_memdeb_asprintf(&dbuf, MEMDEB_SYM, mlp, |
254 | | CODEPTR_FMT(": rtpp_refcnt(%p, %u).incref()", mlp, pub, oldcnt+1)); |
255 | | if (dbuf != NULL) { |
256 | | rtpp_stacktrace_print(dbuf); |
257 | | free(dbuf); |
258 | | } |
259 | | #else |
260 | 0 | fprintf(stderr, CODEPTR_FMT(": rtpp_refcnt(%p, %u).incref()\n", mlp, pub, oldcnt+1)); |
261 | 0 | #endif |
262 | 0 | } |
263 | 3.42M | #endif |
264 | 3.42M | RTPP_DBG_ASSERT(oldcnt >= 0); |
265 | 3.42M | } |
266 | | |
267 | | static int |
268 | | rtpp_refcnt_tryincref(struct rtpp_refcnt *pub) |
269 | 0 | { |
270 | 0 | struct rtpp_refcnt_priv *pvt; |
271 | 0 | int oldcnt; |
272 | |
|
273 | 0 | PUB2PVT(pub, pvt); |
274 | 0 | if (!pvt->shared) { |
275 | 0 | pvt->shared = 1; |
276 | 0 | atomic_store_explicit(&pvt->cnt, 1, memory_order_release); |
277 | 0 | return (0); |
278 | 0 | } |
279 | 0 | oldcnt = atomic_fetch_add_explicit(&pvt->cnt, 1, memory_order_acquire); |
280 | 0 | if (oldcnt < 0) { |
281 | 0 | atomic_fetch_sub_explicit(&pvt->cnt, 1, memory_order_release); |
282 | 0 | return (-1); |
283 | 0 | } |
284 | 0 | RTPP_DBG_ASSERT(oldcnt < RC_ABS_MAX); |
285 | 0 | return (0); |
286 | 0 | } |
287 | | |
288 | | static void |
289 | | rtpp_refcnt_decref(struct rtpp_refcnt *pub, HERETYPE mlp) |
290 | 6.19M | { |
291 | 6.19M | struct rtpp_refcnt_priv *pvt; |
292 | 6.19M | int oldcnt; |
293 | | |
294 | 6.19M | PUB2PVT(pub, pvt); |
295 | 6.19M | RTPP_DBGCODE() { |
296 | 0 | oldcnt = atomic_load_explicit(&pvt->cnt, memory_order_relaxed); |
297 | 0 | RTPP_DBG_ASSERT(oldcnt >= 0 && oldcnt < RC_ABS_MAX); |
298 | 0 | } |
299 | 6.19M | #if RTPP_DEBUG_refcnt |
300 | | /* |
301 | | * Fetch flags before decrement, otherwise we can decrement and then |
302 | | * somebody decrements it and deallocates. Atomic is not needed since |
303 | | * this initialized at the init time. |
304 | | */ |
305 | 6.19M | unsigned int trace = pvt->trace; |
306 | 6.19M | #endif |
307 | 6.19M | if (pvt->shared) { |
308 | 4.13M | oldcnt = atomic_fetch_sub_explicit(&pvt->cnt, 1, memory_order_release); |
309 | 4.13M | } else { |
310 | 2.05M | oldcnt = 0; |
311 | 2.05M | } |
312 | 6.19M | #if RTPP_DEBUG_refcnt |
313 | 6.19M | if (trace) { |
314 | | #ifdef RTPP_DEBUG |
315 | | char *dbuf; |
316 | | rtpp_memdeb_asprintf(&dbuf, MEMDEB_SYM, mlp, |
317 | | CODEPTR_FMT(": rtpp_refcnt(%p, %u).decref()", mlp, pub, oldcnt+1)); |
318 | | if (dbuf != NULL) { |
319 | | rtpp_stacktrace_print(dbuf); |
320 | | free(dbuf); |
321 | | } |
322 | | #else |
323 | 0 | fprintf(stderr, CODEPTR_FMT(": rtpp_refcnt(%p, %u).decref()\n", mlp, pub, oldcnt+1)); |
324 | 0 | #endif |
325 | 0 | } |
326 | 6.19M | #endif |
327 | 6.19M | RTPP_DBG_ASSERT(oldcnt >= 0); |
328 | 6.19M | if (oldcnt == 0) { |
329 | 2.77M | if (pvt->shared) { |
330 | 715k | atomic_thread_fence(memory_order_acquire); |
331 | 715k | } |
332 | 2.77M | int ulen = atomic_load_explicit(&pvt->ulen, memory_order_relaxed); |
333 | 10.0M | for (int i = ulen; i >= 0; i--) { |
334 | 7.30M | struct dtor_pair *dp = &pvt->dtors[i]; |
335 | 7.30M | #if RTPP_DEBUG_refcnt |
336 | 7.30M | if (trace) { |
337 | 0 | Dl_info info; |
338 | 0 | if (dladdr(dp->f, &info) && info.dli_sname != NULL) |
339 | 0 | fprintf(stderr, "calling destructor %s@<%p>(%p)\n", info.dli_sname, |
340 | 0 | dp->f, dp->data); |
341 | 0 | else |
342 | 0 | fprintf(stderr, "calling destructor @<%p>(%p)\n", dp->f, dp->data); |
343 | 0 | } |
344 | 7.30M | #endif |
345 | 7.30M | if (i == 0) |
346 | 2.77M | rtpp_refcnt_fin(pub); |
347 | 7.30M | if (dp->f != NULL) { |
348 | 4.67M | dp->f(dp->data); |
349 | 4.67M | } else { |
350 | 2.63M | struct rtpp_refcnt *other = dp->rcnt; |
351 | 2.63M | rtpp_refcnt_decref(other, mlp); |
352 | 2.63M | } |
353 | 7.30M | } |
354 | 2.77M | } |
355 | 6.19M | } |
356 | | |
357 | | static void * |
358 | | rtpp_refcnt_getdata(struct rtpp_refcnt *pub) |
359 | 818k | { |
360 | 818k | struct rtpp_refcnt_priv *pvt; |
361 | | |
362 | 818k | PUB2PVT(pub, pvt); |
363 | 818k | RTPP_DBG_ASSERT(atomic_load(&pvt->cnt) >= 0 && atomic_load(&pvt->ulen) >= 0); |
364 | 818k | return (pvt->dtors[0].data); |
365 | 818k | } |
366 | | |
367 | | #if RTPP_DEBUG_refcnt |
368 | | static void |
369 | | rtpp_refcnt_traceen(struct rtpp_refcnt *pub, HERETYPE mlp) |
370 | 0 | { |
371 | 0 | struct rtpp_refcnt_priv *pvt; |
372 | |
|
373 | 0 | PUB2PVT(pub, pvt); |
374 | 0 | pvt->trace = 1; |
375 | 0 | int oldcnt = atomic_load_explicit(&pvt->cnt, memory_order_relaxed) + 1; |
376 | 0 | fprintf(stderr, CODEPTR_FMT(": rtpp_refcnt(%p, %u).traceen()\n", mlp, pub, oldcnt)); |
377 | 0 | } |
378 | | |
379 | | static int |
380 | | rtpp_refcnt_peek(struct rtpp_refcnt *pub) |
381 | 43.2k | { |
382 | 43.2k | struct rtpp_refcnt_priv *pvt; |
383 | | |
384 | 43.2k | PUB2PVT(pub, pvt); |
385 | | return atomic_load_explicit(&pvt->cnt, memory_order_relaxed) + 1; |
386 | 43.2k | } |
387 | | #endif |