Coverage Report

Created: 2025-10-28 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pjsip/pjlib/src/pj/pool_caching.c
Line
Count
Source
1
/* 
2
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
3
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
18
 */
19
#include <pj/pool.h>
20
#include <pj/assert.h>
21
#include <pj/log.h>
22
#include <pj/string.h>
23
#include <pj/assert.h>
24
#include <pj/lock.h>
25
#include <pj/os.h>
26
#include <pj/pool_buf.h>
27
28
#if !PJ_HAS_POOL_ALT_API
29
30
static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
31
                                    const char *name,
32
                                    pj_size_t initial_size, 
33
                                    pj_size_t increment_sz,
34
                                    pj_size_t alignment,
35
                                    pj_pool_callback *callback);
36
static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
37
static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
38
static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz);
39
static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz);
40
41
42
static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = 
43
{
44
    256, 512, 1024, 2048, 4096, 8192, 12288, 16384, 
45
    20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
46
};
47
48
/* Index where the search for size should begin.
49
 * Start with pool_sizes[5], which is 8192.
50
 */
51
76
#define START_SIZE  5
52
53
54
PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, 
55
                                   const pj_pool_factory_policy *policy,
56
                                   pj_size_t max_capacity)
57
38
{
58
38
    int i;
59
38
    pj_pool_t *pool;
60
38
    pj_status_t status;
61
62
38
    PJ_CHECK_STACK();
63
64
38
    pj_bzero(cp, sizeof(*cp));
65
    
66
38
    cp->max_capacity = max_capacity;
67
38
    pj_list_init(&cp->used_list);
68
646
    for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
69
608
        pj_list_init(&cp->free_list[i]);
70
71
38
    if (policy == NULL) {
72
0
        policy = &pj_pool_factory_default_policy;
73
0
    }
74
    
75
38
    pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
76
38
    cp->factory.create_pool = &cpool_create_pool;
77
38
    cp->factory.release_pool = &cpool_release_pool;
78
38
    cp->factory.dump_status = &cpool_dump_status;
79
80
    /* Deprecated, these callbacks are only used for updating cp.used_size &
81
     * cp.peak_used_size which are no longer used.
82
     */
83
    //cp->factory.on_block_alloc = &cpool_on_block_alloc;
84
    //cp->factory.on_block_free = &cpool_on_block_free;
85
38
    PJ_UNUSED_ARG(cpool_on_block_alloc);
86
38
    PJ_UNUSED_ARG(cpool_on_block_free);
87
88
38
    pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf));
89
38
    status = pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock);
90
    /* This mostly serves to silent coverity warning about unchecked 
91
     * return value. There's not much we can do if it fails. */
92
38
    PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS, return);
93
38
}
94
95
PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
96
38
{
97
38
    int i;
98
38
    pj_pool_t *pool;
99
100
38
    PJ_CHECK_STACK();
101
102
    /* Delete all pool in free list */
103
646
    for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
104
608
        pj_pool_t *next;
105
608
        pool = (pj_pool_t*) cp->free_list[i].next;
106
608
        for (; pool != (void*)&cp->free_list[i]; pool = next) {
107
0
            next = pool->next;
108
0
            pj_list_erase(pool);
109
0
            pj_pool_destroy_int(pool);
110
0
        }
111
608
    }
112
113
    /* Delete all pools in used list */
114
38
    pool = (pj_pool_t*) cp->used_list.next;
115
38
    while (pool != (pj_pool_t*) &cp->used_list) {
116
0
        pj_pool_t *next = pool->next;
117
0
        pj_list_erase(pool);
118
0
        PJ_LOG(4,(pool->obj_name, 
119
0
                  "Pool is not released by application, releasing now"));
120
0
        pj_pool_destroy_int(pool);
121
0
        pool = next;
122
0
    }
123
124
38
    if (cp->lock) {
125
38
        pj_status_t status;
126
38
        pj_lock_destroy(cp->lock);
127
38
        status = pj_lock_create_null_mutex(NULL, "cachingpool", &cp->lock);
128
38
        PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS, return);
129
38
    }
130
38
}
131
132
static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
133
                                    const char *name, 
134
                                    pj_size_t initial_size, 
135
                                    pj_size_t increment_sz,
136
                                    pj_size_t alignment,
137
                                    pj_pool_callback *callback)
138
38
{
139
38
    pj_caching_pool *cp = (pj_caching_pool*)pf;
140
38
    pj_pool_t *pool;
141
38
    int idx;
142
143
38
    PJ_CHECK_STACK();
144
145
38
    pj_lock_acquire(cp->lock);
146
147
    /* Use pool factory's policy when callback is NULL */
148
38
    if (callback == NULL) {
149
38
        callback = pf->policy.callback;
150
38
    }
151
152
    /* Search the suitable size for the pool. 
153
     * We'll just do linear search to the size array, as the array size itself
154
     * is only a few elements. Binary search I suspect will be less efficient
155
     * for this purpose.
156
     */
157
38
    if (initial_size <= pool_sizes[START_SIZE]) {
158
38
        for (idx=START_SIZE-1; 
159
152
             idx >= 0 && pool_sizes[idx] >= initial_size;
160
114
             --idx)
161
114
            ;
162
38
        ++idx;
163
38
    } else {
164
0
        for (idx=START_SIZE+1; 
165
0
             idx < PJ_CACHING_POOL_ARRAY_SIZE && 
166
0
                  pool_sizes[idx] < initial_size;
167
0
             ++idx)
168
0
            ;
169
0
    }
170
171
    /* Check whether there's a pool in the list. */
172
38
    if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
173
        /* No pool is available. */
174
        /* Set minimum size. */
175
38
        if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
176
38
            initial_size =  pool_sizes[idx];
177
178
        /* Create new pool */
179
38
        pool = pj_pool_create_int(&cp->factory, name, initial_size, 
180
38
                                  increment_sz, alignment, callback);
181
38
        if (!pool) {
182
0
            pj_lock_release(cp->lock);
183
0
            return NULL;
184
0
        }
185
186
38
    } else {
187
        /* Get one pool from the list. */
188
0
        pool = (pj_pool_t*) cp->free_list[idx].next;
189
0
        pj_list_erase(pool);
190
191
        /* Initialize the pool. */
192
0
        pj_pool_init_int(pool, name, increment_sz, alignment, callback);
193
194
        /* Update pool manager's free capacity. */
195
0
        if (cp->capacity > pj_pool_get_capacity(pool)) {
196
0
            cp->capacity -= pj_pool_get_capacity(pool);
197
0
        } else {
198
0
            cp->capacity = 0;
199
0
        }
200
201
0
        PJ_LOG(6, (pool->obj_name, "pool reused, size=%lu",
202
0
                   (unsigned long)pool->capacity));
203
0
    }
204
205
    /* Put in used list. */
206
38
    pj_list_insert_before( &cp->used_list, pool );
207
208
    /* Mark factory data */
209
38
    pool->factory_data = (void*) (pj_ssize_t) idx;
210
211
    /* Increment used count. */
212
38
    ++cp->used_count;
213
214
38
    pj_lock_release(cp->lock);
215
38
    return pool;
216
38
}
217
218
static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
219
38
{
220
38
    pj_caching_pool *cp = (pj_caching_pool*)pf;
221
38
    pj_size_t pool_capacity;
222
38
    unsigned i;
223
224
38
    PJ_CHECK_STACK();
225
226
38
    PJ_ASSERT_ON_FAIL(pf && pool, return);
227
228
38
    pj_lock_acquire(cp->lock);
229
230
#if PJ_SAFE_POOL
231
    /* Make sure pool is still in our used list */
232
    if (pj_list_find_node(&cp->used_list, pool) != pool) {
233
        pj_lock_release(cp->lock);
234
        pj_assert(!"Attempt to destroy pool that has been destroyed before");
235
        return;
236
    }
237
#endif
238
239
    /* Erase from the used list. */
240
38
    pj_list_erase(pool);
241
242
    /* Decrement used count. */
243
38
    --cp->used_count;
244
245
38
    pool_capacity = pj_pool_get_capacity(pool);
246
247
    /* Destroy the pool if the size is greater than our size or if the total
248
     * capacity in our recycle list (plus the size of the pool) exceeds 
249
     * maximum capacity.
250
   . */
251
38
    if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
252
38
        cp->capacity + pool_capacity > cp->max_capacity)
253
38
    {
254
38
        pj_pool_destroy_int(pool);
255
38
        pj_lock_release(cp->lock);
256
38
        return;
257
38
    }
258
259
    /* Reset pool. */
260
0
    PJ_LOG(6, (pool->obj_name, "recycle(): cap=%lu, used=%lu(%lu%%)", 
261
0
               (unsigned long)pool_capacity,
262
0
               (unsigned long)pj_pool_get_used_size(pool), 
263
0
               (unsigned long)(pj_pool_get_used_size(pool)*100/
264
0
                               pool_capacity)));
265
0
    pj_pool_reset(pool);
266
267
0
    pool_capacity = pj_pool_get_capacity(pool);
268
269
    /*
270
     * Otherwise put the pool in our recycle list.
271
     */
272
0
    i = (unsigned) (unsigned long) (pj_ssize_t) pool->factory_data;
273
274
0
    pj_assert(i<PJ_CACHING_POOL_ARRAY_SIZE);
275
0
    if (i >= PJ_CACHING_POOL_ARRAY_SIZE ) {
276
        /* Something has gone wrong with the pool. */
277
0
        pj_pool_destroy_int(pool);
278
0
        pj_lock_release(cp->lock);
279
0
        return;
280
0
    }
281
282
0
    pj_list_insert_after(&cp->free_list[i], pool);
283
0
    cp->capacity += pool_capacity;
284
285
0
    pj_lock_release(cp->lock);
286
0
}
287
288
static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
289
0
{
290
0
#if PJ_LOG_MAX_LEVEL >= 3
291
0
    pj_caching_pool *cp = (pj_caching_pool*)factory;
292
293
0
    pj_lock_acquire(cp->lock);
294
295
0
    PJ_LOG(3,("cachpool", " Dumping caching pool:"));
296
0
    PJ_LOG(3,("cachpool", "   Capacity=%lu, max_capacity=%lu, used_cnt=%lu",
297
0
              (unsigned long)cp->capacity, (unsigned long)cp->max_capacity,
298
0
              (unsigned long)cp->used_count));
299
0
    if (detail) {
300
0
        pj_pool_t *pool = (pj_pool_t*) cp->used_list.next;
301
0
        pj_size_t total_used = 0, total_capacity = 0;
302
0
        PJ_LOG(3,("cachpool", "  Dumping all active pools:"));
303
0
        while (pool != (void*)&cp->used_list) {
304
0
            pj_size_t pool_capacity = pj_pool_get_capacity(pool);
305
0
            pj_pool_block *block = pool->block_list.next;
306
0
            unsigned nblocks = 0;
307
308
0
            while (block != &pool->block_list) {
309
#if 0
310
                PJ_LOG(6, ("cachpool", "   %16s block %u, size %ld",
311
                                       pj_pool_getobjname(pool), nblocks,
312
                                       (long)(block->end - block->buf + 1)));
313
#endif
314
0
                nblocks++;
315
0
                block = block->next;
316
0
            }
317
318
0
            PJ_LOG(3,("cachpool", "   %16s: %8lu of %8lu (%lu%%) used, "
319
0
                                  "nblocks: %d",
320
0
                                  pj_pool_getobjname(pool), 
321
0
                                  (unsigned long)pj_pool_get_used_size(pool), 
322
0
                                  (unsigned long)pool_capacity,
323
0
                                  (unsigned long)(pj_pool_get_used_size(pool)*
324
0
                                                  100/pool_capacity),
325
0
                                  nblocks));
326
327
#if PJ_POOL_MAX_SEARCH_BLOCK_COUNT == 0
328
            if (nblocks >= 10) {
329
                PJ_LOG(3,("cachpool", "   %16s has too many blocks (%d), "
330
                                      "consider increasing its initial and/or "
331
                                      "increment size for better performance",
332
                                      pj_pool_getobjname(pool), nblocks));
333
            }
334
#endif
335
336
0
            total_used += pj_pool_get_used_size(pool);
337
0
            total_capacity += pool_capacity;
338
0
            pool = pool->next;
339
0
        }
340
0
        if (total_capacity) {
341
0
            PJ_LOG(3,("cachpool", "  Total %9lu of %9lu (%lu %%) used!",
342
0
                                  (unsigned long)total_used,
343
0
                                  (unsigned long)total_capacity,
344
0
                                  (unsigned long)(total_used * 100 /
345
0
                                                  total_capacity)));
346
0
        }
347
0
    }
348
349
0
    pj_lock_release(cp->lock);
350
#else
351
    PJ_UNUSED_ARG(factory);
352
    PJ_UNUSED_ARG(detail);
353
#endif
354
0
}
355
356
357
static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz)
358
0
{
359
0
    pj_caching_pool *cp = (pj_caching_pool*)f;
360
361
    //Can't lock because mutex is not recursive
362
    //if (cp->mutex) pj_mutex_lock(cp->mutex);
363
364
0
    cp->used_size += sz;
365
0
    if (cp->used_size > cp->peak_used_size)
366
0
        cp->peak_used_size = cp->used_size;
367
368
    //if (cp->mutex) pj_mutex_unlock(cp->mutex);
369
370
0
    return PJ_TRUE;
371
0
}
372
373
374
static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz)
375
0
{
376
0
    pj_caching_pool *cp = (pj_caching_pool*)f;
377
378
    //pj_mutex_lock(cp->mutex);
379
0
    cp->used_size -= sz;
380
    //pj_mutex_unlock(cp->mutex);
381
0
}
382
383
384
#endif  /* PJ_HAS_POOL_ALT_API */
385