Coverage Report

Created: 2026-09-14 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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