Coverage Report

Created: 2026-08-13 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rtpproxy/src/rtpp_queue.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2014-2019 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 vasprintf(3) */
30
#define _GNU_SOURCE
31
#endif
32
33
#include <assert.h>
34
#include <pthread.h>
35
#include <stdarg.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
40
#include "rtpp_types.h"
41
#include "rtpp_codeptr.h"
42
#include "rtpp_refcnt.h"
43
#include "rtpp_queue.h"
44
#include "rtpp_mallocs.h"
45
#include "rtpp_wi.h"
46
#include "rtpp_debug.h"
47
#include "rtpp_time.h"
48
49
#define RTPQ_DEBUG 0
50
51
typedef struct {
52
    unsigned int buflen;
53
    unsigned int head;
54
    unsigned int tail;
55
    struct rtpp_wi *buffer[0];
56
} circ_buf_t;
57
58
static int
59
circ_buf_isempty(const circ_buf_t *c)
60
435k
{
61
62
435k
    return (c->head == c->tail); /* if the head == tail, we don't have any data */
63
435k
}
64
65
static int
66
circ_buf_push(circ_buf_t *c, struct rtpp_wi *data)
67
435k
{
68
435k
    unsigned int next;
69
70
435k
    next = c->head + 1;  /* next is where head will point to after this write. */
71
435k
    if (next == c->buflen)
72
2.89k
        next = 0;
73
74
435k
    if (next == c->tail)  /* if the head + 1 == tail, circular buffer is full */
75
0
        return(-1);
76
77
#if RTPQ_DEBUG
78
    assert(c->buffer[c->head] == NULL);
79
#endif
80
435k
    c->buffer[c->head] = data;  /* Load data and then move */
81
435k
    c->head = next;             /* head to next data offset. */
82
435k
    return(0);  /* return success to indicate successful push. */
83
435k
}
84
85
static unsigned int
86
circ_buf_popmany(circ_buf_t *c, struct rtpp_wi *data[], unsigned int howmany)
87
359k
{
88
359k
    unsigned int next;
89
359k
    unsigned int last;
90
359k
    unsigned int copyn;
91
359k
    unsigned int rval;
92
93
359k
    rval = 0;
94
359k
    RTPP_DBG_ASSERT(howmany > 0);
95
393k
    while (!circ_buf_isempty(c)) {
96
359k
        next = last = c->tail + howmany - rval;
97
359k
        if (c->head < c->tail) {
98
58.1k
            if (last >= c->buflen) {
99
2.62k
                last = c->buflen;
100
2.62k
                next = 0;
101
2.62k
            }
102
301k
        } else {
103
301k
            if (last > c->head) {
104
33.8k
                last = c->head;
105
33.8k
                next = c->head;
106
33.8k
            }
107
301k
        }
108
359k
        copyn = last - c->tail;
109
359k
        memcpy(data, &(c->buffer[c->tail]), copyn * sizeof(data[0]));
110
#if RTPQ_DEBUG
111
        memset(&(c->buffer[c->tail]), '\0', copyn * sizeof(data[0]));
112
#endif
113
359k
        c->tail = next;
114
359k
        rval += copyn;
115
359k
        if (rval == howmany)
116
325k
            break;
117
33.9k
        data += copyn;
118
33.9k
    }
119
#if RTPQ_DEBUG
120
    assert(rval <= howmany);
121
    assert(c->tail < c->buflen);
122
#endif
123
124
359k
    return(rval); /* Return number of objects popped */
125
359k
}
126
127
static int
128
circ_buf_peek(const circ_buf_t *c, unsigned int offset, struct rtpp_wi **data)
129
28.3k
{
130
28.3k
    unsigned int itmidx, clen;
131
132
28.3k
    if (circ_buf_isempty(c))
133
0
        return(-1);
134
135
28.3k
    if (c->head < c->tail) {
136
4.51k
       clen = (c->head + c->buflen) - c->tail;
137
23.8k
    } else {
138
23.8k
       clen = c->head - c->tail;
139
23.8k
    }
140
28.3k
    if (offset >= clen)
141
1.54k
        return(-1);
142
143
26.8k
    itmidx = c->tail + offset;  /* itmidx points to the item in question */
144
26.8k
    if(itmidx >= c->buflen)
145
1.20k
        itmidx -= c->buflen;
146
#if RTPQ_DEBUG
147
    assert(itmidx < c->buflen);
148
    assert(c->buffer[itmidx] != NULL);
149
#endif
150
151
26.8k
    *data = c->buffer[itmidx];  /* Read data and then move */
152
26.8k
    return(0);  /* return success to indicate successful pop. */
153
28.3k
}
154
155
static int
156
circ_buf_replace(circ_buf_t *c, unsigned int offset, struct rtpp_wi **data)
157
9.32k
{
158
9.32k
    unsigned int itmidx, clen;
159
9.32k
    struct rtpp_wi *tdata;
160
161
9.32k
    if (circ_buf_isempty(c))
162
0
        return(-1);
163
164
9.32k
    if (c->head < c->tail) {
165
1.65k
       clen = (c->head + c->buflen) - c->tail;
166
7.67k
    } else {
167
7.67k
       clen = c->head - c->tail;
168
7.67k
    }
169
9.32k
    if (offset >= clen)
170
0
        return(-1);
171
172
9.32k
    itmidx = c->tail + offset;  /* itmidx points to the item in question */
173
9.32k
    if(itmidx >= c->buflen)
174
828
        itmidx -= c->buflen;
175
#if RTPQ_DEBUG
176
    assert(itmidx < c->buflen);
177
    assert(c->buffer[itmidx] != NULL);
178
#endif
179
9.32k
    tdata = c->buffer[itmidx];
180
#if RTPQ_DEBUG
181
    assert(tdata != NULL);
182
#endif
183
9.32k
    c->buffer[itmidx] = *data;  /* Read data and then replace */
184
9.32k
    *data = tdata;
185
9.32k
    return(0);  /* return success to indicate successful pop. */
186
9.32k
}
187
188
static int
189
circ_buf_remove(circ_buf_t *c, unsigned int offset)
190
4.65k
{
191
4.65k
    unsigned int clen;
192
4.65k
    struct rtpp_wi *data;
193
194
4.65k
    if (circ_buf_isempty(c))
195
0
        return(-1);
196
197
4.65k
    if (c->head < c->tail) {
198
823
       clen = (c->head + c->buflen) - c->tail;
199
3.83k
    } else {
200
3.83k
       clen = c->head - c->tail;
201
3.83k
    }
202
4.65k
    if (offset >= clen)
203
0
        return(-1);
204
205
13.9k
    for (; offset > 0; offset--) {
206
9.32k
        assert(circ_buf_peek(c, offset - 1, &data) == 0);
207
9.32k
        assert(circ_buf_replace(c, offset, &data) == 0);
208
9.32k
    }
209
#if RTPQ_DEBUG
210
    assert(c->buffer[c->tail] != NULL);
211
    c->buffer[c->tail] = NULL;
212
#endif
213
4.65k
    c->tail += 1;
214
4.65k
    if (c->tail == c->buflen)
215
273
        c->tail = 0;
216
4.65k
    return(0);  /* return success to indicate successful removal. */
217
4.65k
}
218
219
struct rtpp_queue
220
{
221
    struct rtpp_wi *head;
222
    struct rtpp_wi *tail;
223
    pthread_cond_t cond;
224
    pthread_mutex_t mutex;
225
    unsigned int length;
226
    unsigned int qlen;
227
    unsigned int mlen;
228
    circ_buf_t circb;
229
    char name[128];
230
};
231
232
struct rtpp_queue *
233
rtpp_queue_init(unsigned int cb_capacity, const char *fmt, ...)
234
18
{
235
18
    struct rtpp_queue *queue;
236
18
    unsigned int cb_buflen;
237
18
    va_list ap;
238
18
    int eval;
239
18
    pthread_condattr_t cond_attr;
240
241
18
    cb_buflen = cb_capacity + 1;
242
18
    queue = rtpp_zmalloc(sizeof(*queue) + (sizeof(queue->circb.buffer[0]) * cb_buflen));
243
18
    if (queue == NULL)
244
0
        goto e0;
245
246
    /* Set the clock type for the condition variable to CLOCK_MONOTONIC */
247
18
    if (pthread_condattr_init(&cond_attr) != 0) {
248
0
        goto e1;
249
0
    }
250
18
    if (pthread_condattr_setclock(&cond_attr, CLOCK_MONOTONIC) != 0) {
251
0
        goto e2;
252
0
    }
253
18
    if ((eval = pthread_cond_init(&queue->cond, &cond_attr)) != 0) {
254
0
        goto e2;
255
0
    }
256
18
    if (pthread_mutex_init(&queue->mutex, NULL) != 0) {
257
0
        goto e3;
258
0
    }
259
18
    va_start(ap, fmt);
260
18
    int r = vsnprintf(queue->name, sizeof(queue->name), fmt, ap);
261
18
    va_end(ap);
262
18
    if (r >= sizeof(queue->name)) {
263
0
        goto e4;
264
0
    }
265
18
    queue->qlen = 1;
266
18
    queue->mlen = -1;
267
18
    queue->circb.buflen = cb_buflen;
268
18
    pthread_condattr_destroy(&cond_attr);
269
18
    return (queue);
270
0
e4:
271
0
    pthread_mutex_destroy(&queue->mutex);
272
0
e3:
273
0
    pthread_cond_destroy(&queue->cond);
274
0
e2:
275
0
    pthread_condattr_destroy(&cond_attr);
276
0
e1:
277
0
    free(queue);
278
0
e0:
279
0
    return (NULL);
280
0
}
281
282
int
283
rtpp_queue_setmaxlen(struct rtpp_queue *queue, unsigned int new_mlen)
284
8
{
285
286
8
    pthread_mutex_lock(&queue->mutex);
287
8
    int mlen = queue->mlen;
288
8
    queue->mlen = new_mlen;
289
8
    pthread_mutex_unlock(&queue->mutex);
290
8
    return (mlen);
291
8
}
292
293
void
294
rtpp_queue_destroy(struct rtpp_queue *queue)
295
18
{
296
18
    while (rtpp_queue_get_length(queue) > 0) {
297
0
        struct rtpp_wi *wip;
298
0
        wip = rtpp_queue_get_item(queue, 0);
299
0
        RTPP_OBJ_DECREF(wip);
300
0
    }
301
18
    pthread_cond_destroy(&queue->cond);
302
18
    pthread_mutex_destroy(&queue->mutex);
303
18
    free(queue);
304
18
}
305
306
static int
307
rtpp_queue_getclen(const struct rtpp_queue *queue)
308
920k
{
309
920k
    int clen;
310
311
920k
    clen = queue->length;
312
920k
    if (queue->circb.head < queue->circb.tail) {
313
133k
       clen += (queue->circb.head + queue->circb.buflen) - queue->circb.tail;
314
786k
    } else if (queue->circb.head > queue->circb.tail) {
315
677k
       clen += queue->circb.head - queue->circb.tail;
316
677k
    }
317
318
920k
    return (clen);
319
920k
}
320
321
static int
322
rtpp_queue_extract_items(struct rtpp_queue *queue, struct rtpp_wi **items, int ilen)
323
359k
{
324
359k
    int i, j;
325
326
359k
    i = circ_buf_popmany(&queue->circb, items, ilen);
327
359k
    if ((i == ilen) || (queue->length == 0))
328
359k
        return (i);
329
171
    items += i;
330
171
    ilen -= i;
331
171
    for (j = 0; j < ilen; j++) {
332
0
        items[j] = queue->head;
333
0
        queue->head = items[j]->next;
334
0
        if (queue->head == NULL) {
335
0
            queue->tail = NULL;
336
0
            j += 1;
337
0
            break;
338
0
        }
339
0
    }
340
171
    queue->length -= j;
341
171
    i += j;
342
171
    return (i);
343
359k
}
344
345
unsigned int
346
rtpp_queue_setqlen(struct rtpp_queue *queue, unsigned int qlen)
347
4
{
348
4
    unsigned int rval;
349
350
4
    pthread_mutex_lock(&queue->mutex);
351
4
    rval = queue->qlen;
352
4
    queue->qlen = qlen;
353
4
    pthread_mutex_unlock(&queue->mutex);
354
4
    return (rval);
355
4
}
356
357
int
358
rtpp_queue_put_item(struct rtpp_wi *wi, struct rtpp_queue *queue)
359
474k
{
360
474k
    int rval = 0;
361
362
474k
    pthread_mutex_lock(&queue->mutex);
363
    /*
364
     * If queue is not empty, push to the queue so that order of elements
365
     * is preserved while pulling them out.
366
     */
367
474k
    if (queue->mlen != -1 && rtpp_queue_getclen(queue) >= queue->mlen) {
368
38.2k
        rval = -1;
369
38.2k
        goto out;
370
38.2k
    }
371
435k
    if ((queue->length > 0) || (circ_buf_push(&queue->circb, wi) != 0)) {
372
0
        RTPPQ_APPEND(queue, wi);
373
#if 0
374
        if (queue->length > 99 && queue->length % 100 == 0)
375
            fprintf(stderr, "queue(%s): length %d\n", queue->name, queue->length);
376
#endif
377
0
    }
378
379
435k
    if ((queue->qlen == 1) || (queue->qlen > 1 && rtpp_queue_getclen(queue) % queue->qlen == 0) || wi->wi_type == RTPP_WI_TYPE_SGNL) {
380
        /* notify worker thread */
381
431k
        pthread_cond_signal(&queue->cond);
382
431k
    }
383
384
474k
out:
385
474k
    pthread_mutex_unlock(&queue->mutex);
386
474k
    return (rval);
387
435k
}
388
389
void
390
rtpp_queue_pump(struct rtpp_queue *queue)
391
10.4k
{
392
393
10.4k
    pthread_mutex_lock(&queue->mutex);
394
10.4k
    if (rtpp_queue_getclen(queue) > 0) {
395
        /* notify worker thread */
396
1.24k
        pthread_cond_signal(&queue->cond);
397
1.24k
    }
398
399
10.4k
    pthread_mutex_unlock(&queue->mutex);
400
10.4k
}
401
402
void
403
rtpp_queue_wakeup(struct rtpp_queue *queue)
404
0
{
405
406
0
    pthread_mutex_lock(&queue->mutex);
407
    /* notify worker thread */
408
0
    pthread_cond_signal(&queue->cond);
409
0
    pthread_mutex_unlock(&queue->mutex);
410
0
}
411
412
struct rtpp_wi *
413
rtpp_queue_get_item_by(struct rtpp_queue *queue, struct timespec *deadline, int *rval)
414
0
{
415
0
    struct rtpp_wi *wi;
416
417
0
    if (rtpp_queue_get_items_by(queue, &wi, 1, deadline, rval) == 0)
418
0
        return (NULL);
419
0
    wi->next = NULL;
420
0
    return (wi);
421
0
}
422
423
struct rtpp_wi *
424
rtpp_queue_get_item(struct rtpp_queue *queue, int return_on_wake)
425
325k
{
426
325k
    struct rtpp_wi *wi;
427
428
325k
    if (rtpp_queue_get_items(queue, &wi, 1, return_on_wake) == 0)
429
0
        return (NULL);
430
325k
    wi->next = NULL;
431
325k
    return (wi);
432
325k
}
433
434
int
435
rtpp_queue_get_items(struct rtpp_queue *queue, struct rtpp_wi **items, int ilen, int return_on_wake)
436
359k
{
437
359k
    int i;
438
439
359k
    pthread_mutex_lock(&queue->mutex);
440
393k
    while (rtpp_queue_getclen(queue) == 0) {
441
34.5k
        pthread_cond_wait(&queue->cond, &queue->mutex);
442
34.5k
        if (rtpp_queue_getclen(queue) == 0 && return_on_wake != 0) {
443
0
            pthread_mutex_unlock(&queue->mutex);
444
0
            return (0);
445
0
        }
446
34.5k
    }
447
359k
    i = rtpp_queue_extract_items(queue, items, ilen);
448
359k
    pthread_mutex_unlock(&queue->mutex);
449
359k
    return (i);
450
359k
}
451
452
int
453
rtpp_queue_get_items_by(struct rtpp_queue *queue, struct rtpp_wi **items, int ilen,
454
  struct timespec *deadline, int *rval)
455
0
{
456
0
    int i, rc;
457
458
0
    pthread_mutex_lock(&queue->mutex);
459
0
    while (rtpp_queue_getclen(queue) == 0) {
460
0
        rc = pthread_cond_timedwait(&queue->cond, &queue->mutex, deadline);
461
0
        if (rtpp_queue_getclen(queue) != 0)
462
0
            break;
463
0
        if (rval != NULL)
464
0
            *rval = rc;
465
0
        pthread_mutex_unlock(&queue->mutex);
466
0
        return (0);
467
0
    }
468
0
    i = rtpp_queue_extract_items(queue, items, ilen);
469
0
    pthread_mutex_unlock(&queue->mutex);
470
0
    return (i);
471
0
}
472
473
int
474
rtpp_queue_get_length(struct rtpp_queue *queue)
475
117k
{
476
117k
    int length;
477
478
117k
    pthread_mutex_lock(&queue->mutex);
479
117k
    length = rtpp_queue_getclen(queue);
480
117k
    pthread_mutex_unlock(&queue->mutex);
481
117k
    return (length);
482
117k
}
483
484
#if 0
485
int
486
rtpp_queue_count_matching(struct rtpp_queue *queue, rtpp_queue_match_fn_t match_fn, void *fn_args)
487
{
488
    struct rtpp_wi *wi;
489
    int mcnt;
490
491
    mcnt = 0;
492
    pthread_mutex_lock(&queue->mutex);
493
    for (wi = queue->head; wi != NULL; wi = wi->next) {
494
        if (match_fn(wi, fn_args) == 0) {
495
            mcnt++;
496
        }
497
    }
498
    pthread_mutex_unlock(&queue->mutex);
499
    return (mcnt);
500
}
501
#endif
502
503
struct rtpp_wi *
504
rtpp_queue_get_first_matching(struct rtpp_queue *queue, rtpp_queue_match_fn_t match_fn, void *fn_args)
505
6.19k
{
506
6.19k
    struct rtpp_wi *wi, *wi_prev;
507
6.19k
    int i;
508
509
6.19k
    pthread_mutex_lock(&queue->mutex);
510
19.0k
    for (i = 0;; i++) {
511
19.0k
        if (circ_buf_peek(&queue->circb, i, &wi) != 0)
512
1.54k
            break;
513
17.5k
        if (match_fn(wi, fn_args) == 0) {
514
4.65k
            assert(circ_buf_remove(&queue->circb, i) == 0);
515
4.65k
            pthread_mutex_unlock(&queue->mutex);
516
4.65k
            return (wi);
517
4.65k
        }
518
17.5k
    }
519
1.54k
    wi_prev = NULL;
520
1.54k
    for (wi = queue->head; wi != NULL; wi_prev = wi, wi = wi->next) {
521
0
        if (match_fn(wi, fn_args) == 0) {
522
0
            RTPPQ_REMOVE_AFTER(queue, wi_prev);
523
0
            pthread_mutex_unlock(&queue->mutex);
524
0
            return (wi);
525
0
        }
526
0
    }
527
1.54k
    pthread_mutex_unlock(&queue->mutex);
528
    return (NULL);
529
1.54k
}