Coverage Report

Created: 2024-07-23 06:24

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