Coverage Report

Created: 2025-08-28 07:07

/src/pjsip/pjlib/src/pj/timer.c
Line
Count
Source (jump to first uncovered line)
1
/* 
2
 * The PJLIB's timer heap is based (or more correctly, copied and modied)
3
 * from ACE library by Douglas C. Schmidt. ACE is an excellent OO framework
4
 * that implements many core patterns for concurrent communication software.
5
 * If you're looking for C++ alternative of PJLIB, then ACE is your best
6
 * solution.
7
 *
8
 * You may use this file according to ACE open source terms or PJLIB open
9
 * source terms. You can find the fine ACE library at:
10
 *  http://www.cs.wustl.edu/~schmidt/ACE.html
11
 *
12
 * ACE is Copyright (C)1993-2006 Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
13
 *
14
 * GNU Public License:
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
28
 */
29
#include <pj/timer.h>
30
#include <pj/pool.h>
31
#include <pj/os.h>
32
#include <pj/string.h>
33
#include <pj/assert.h>
34
#include <pj/errno.h>
35
#include <pj/lock.h>
36
#include <pj/log.h>
37
#include <pj/rand.h>
38
#include <pj/limits.h>
39
40
#define THIS_FILE       "timer.c"
41
42
0
#define HEAP_PARENT(X)  (X == 0 ? 0 : (((X) - 1) / 2))
43
0
#define HEAP_LEFT(X)    (((X)+(X))+1)
44
45
46
5.01k
#define DEFAULT_MAX_TIMED_OUT_PER_POLL  (64)
47
48
/* Enable this to raise assertion in order to catch bug of timer entry
49
 * which has been deallocated without being cancelled. If disabled,
50
 * the timer heap will simply remove the destroyed entry (and print log)
51
 * and resume normally.
52
 * This setting only works if PJ_TIMER_USE_COPY is enabled.
53
 */
54
#define ASSERT_IF_ENTRY_DESTROYED (PJ_TIMER_USE_COPY? 0: 0)
55
56
57
enum
58
{
59
    F_DONT_CALL = 1,
60
    F_DONT_ASSERT = 2,
61
    F_SET_ID = 4
62
};
63
64
#if PJ_TIMER_USE_COPY
65
66
/* Duplicate/copy of the timer entry. */
67
typedef struct pj_timer_entry_dup
68
{
69
#if PJ_TIMER_USE_LINKED_LIST
70
    /**
71
    * Standard list members.
72
    */
73
    PJ_DECL_LIST_MEMBER(struct pj_timer_entry_dup);
74
#endif
75
76
    /**
77
     * The duplicate copy.
78
     */
79
    pj_timer_entry  dup;
80
81
    /**
82
     * Pointer of the original timer entry.
83
     */
84
    pj_timer_entry *entry;
85
86
    /** 
87
     * The future time when the timer expires, which the value is updated
88
     * by timer heap when the timer is scheduled.
89
     */
90
    pj_time_val     _timer_value;
91
92
    /**
93
     * Internal: the group lock used by this entry, set when
94
     * pj_timer_heap_schedule_w_lock() is used.
95
     */
96
    pj_grp_lock_t  *_grp_lock;
97
98
#if PJ_TIMER_DEBUG
99
    const char     *src_file;
100
    int             src_line;
101
#endif
102
103
} pj_timer_entry_dup;
104
105
0
#define GET_TIMER(ht, node) &ht->timer_dups[node->_timer_id]
106
0
#define GET_ENTRY(node) node->entry
107
0
#define GET_FIELD(node, _timer_id) node->dup._timer_id
108
109
#else
110
111
typedef pj_timer_entry pj_timer_entry_dup;
112
113
#define GET_TIMER(ht, node) node
114
#define GET_ENTRY(node) node
115
#define GET_FIELD(node, _timer_id) node->_timer_id
116
117
#endif
118
119
/**
120
 * The implementation of timer heap.
121
 */
122
struct pj_timer_heap_t
123
{
124
    /** Pool from which the timer heap resize will get the storage from */
125
    pj_pool_t *pool;
126
127
    /** Maximum size of the heap. */
128
    pj_size_t max_size;
129
130
    /** Current size of the heap. */
131
    pj_size_t cur_size;
132
133
    /** Max timed out entries to process per poll. */
134
    unsigned max_entries_per_poll;
135
136
    /** Lock object. */
137
    pj_lock_t *lock;
138
139
    /** Autodelete lock. */
140
    pj_bool_t auto_delete_lock;
141
142
    /**
143
     * Current contents of the Heap, which is organized as a "heap" of
144
     * pj_timer_entry *'s.  In this context, a heap is a "partially
145
     * ordered, almost complete" binary tree, which is stored in an
146
     * array.
147
     */
148
    pj_timer_entry_dup **heap;
149
150
#if PJ_TIMER_USE_LINKED_LIST
151
    /**
152
    * If timer heap uses linked list, then this will represent the head of
153
    * the list.
154
    */
155
    pj_timer_entry_dup head_list;
156
#endif
157
158
    /**
159
     * An array of "pointers" that allows each pj_timer_entry in the
160
     * <heap_> to be located in O(1) time.  Basically, <timer_id_[i]>
161
     * contains the slot in the <heap_> array where an pj_timer_entry
162
     * with timer id <i> resides.  Thus, the timer id passed back from
163
     * <schedule_entry> is really an slot into the <timer_ids> array.  The
164
     * <timer_ids_> array serves two purposes: negative values are
165
     * treated as "pointers" for the <freelist_>, whereas positive
166
     * values are treated as "pointers" into the <heap_> array.
167
     */
168
    pj_timer_id_t *timer_ids;
169
170
    /**
171
     * An array of timer entry copies.
172
     */
173
    pj_timer_entry_dup *timer_dups;
174
175
    /**
176
     * "Pointer" to the first element in the freelist contained within
177
     * the <timer_ids_> array, which is organized as a stack.
178
     */
179
    pj_timer_id_t timer_ids_freelist;
180
181
    /** Callback to be called when a timer expires. */
182
    pj_timer_heap_callback *callback;
183
184
};
185
186
187
188
PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
189
5.01k
{
190
5.01k
    if (ht->lock) {
191
5.01k
        pj_lock_acquire(ht->lock);
192
5.01k
    }
193
5.01k
}
194
195
PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
196
5.01k
{
197
5.01k
    if (ht->lock) {
198
5.01k
        pj_lock_release(ht->lock);
199
5.01k
    }
200
5.01k
}
201
202
203
static void copy_node( pj_timer_heap_t *ht, pj_size_t slot, 
204
                       pj_timer_entry_dup *moved_node )
205
0
{
206
0
    PJ_CHECK_STACK();
207
208
    // Insert <moved_node> into its new location in the heap.
209
0
    ht->heap[slot] = moved_node;
210
211
    // Update the corresponding slot in the parallel <timer_ids_> array.
212
0
    ht->timer_ids[GET_FIELD(moved_node, _timer_id)] = (int)slot;
213
0
}
214
215
static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
216
0
{
217
    // We need to truncate this to <int> for backwards compatibility.
218
0
    pj_timer_id_t new_id = ht->timer_ids_freelist;
219
220
0
    PJ_CHECK_STACK();
221
222
    // The freelist values in the <timer_ids_> are negative, so we need
223
    // to negate them to get the next freelist "pointer."
224
0
    ht->timer_ids_freelist =
225
0
        -ht->timer_ids[ht->timer_ids_freelist];
226
227
0
    return new_id;
228
229
0
}
230
231
static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
232
0
{
233
0
    PJ_CHECK_STACK();
234
235
    // The freelist values in the <timer_ids_> are negative, so we need
236
    // to negate them to get the next freelist "pointer."
237
0
    ht->timer_ids[old_id] = -ht->timer_ids_freelist;
238
0
    ht->timer_ids_freelist = old_id;
239
0
}
240
241
242
static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node,
243
                        size_t slot, size_t child)
244
0
{
245
0
    PJ_CHECK_STACK();
246
247
    // Restore the heap property after a deletion.
248
249
0
    while (child < ht->cur_size)
250
0
    {
251
        // Choose the smaller of the two children.
252
0
        if (child + 1 < ht->cur_size &&
253
0
            PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value,
254
0
                           ht->heap[child]->_timer_value))
255
0
        {
256
0
            child++;
257
0
        }
258
259
        // Perform a <copy> if the child has a larger timeout value than
260
        // the <moved_node>.
261
0
        if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value,
262
0
                           moved_node->_timer_value))
263
0
        {
264
0
            copy_node( ht, slot, ht->heap[child]);
265
0
            slot = child;
266
0
            child = HEAP_LEFT(child);
267
0
        }
268
0
        else
269
            // We've found our location in the heap.
270
0
            break;
271
0
    }
272
273
0
    copy_node( ht, slot, moved_node);
274
0
}
275
276
static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node,
277
                       size_t slot, size_t parent)
278
0
{
279
    // Restore the heap property after an insertion.
280
281
0
    while (slot > 0)
282
0
    {
283
        // If the parent node is greater than the <moved_node> we need
284
        // to copy it down.
285
0
        if (PJ_TIME_VAL_LT(moved_node->_timer_value,
286
0
                           ht->heap[parent]->_timer_value))
287
0
        {
288
0
            copy_node(ht, slot, ht->heap[parent]);
289
0
            slot = parent;
290
0
            parent = HEAP_PARENT(slot);
291
0
        }
292
0
        else
293
0
            break;
294
0
    }
295
296
    // Insert the new node into its proper resting place in the heap and
297
    // update the corresponding slot in the parallel <timer_ids> array.
298
0
    copy_node(ht, slot, moved_node);
299
0
}
300
301
302
static pj_timer_entry_dup * remove_node( pj_timer_heap_t *ht, size_t slot)
303
0
{
304
0
    pj_timer_entry_dup *removed_node = ht->heap[slot];
305
306
    // Return this timer id to the freelist.
307
0
    push_freelist( ht, GET_FIELD(removed_node, _timer_id) );
308
309
    // Decrement the size of the heap by one since we're removing the
310
    // "slot"th node.
311
0
    ht->cur_size--;
312
313
    // Set the ID
314
0
    if (GET_FIELD(removed_node, _timer_id) !=
315
0
        GET_ENTRY(removed_node)->_timer_id)
316
0
    {
317
0
#if PJ_TIMER_DEBUG
318
0
        PJ_LOG(3,(THIS_FILE, "Bug! Trying to remove entry %p from %s "
319
0
                             "line %d, which has been deallocated "
320
0
                             "without being cancelled",
321
0
                             GET_ENTRY(removed_node),
322
0
                             removed_node->src_file,
323
0
                             removed_node->src_line));
324
#else
325
        PJ_LOG(3,(THIS_FILE, "Bug! Trying to remove entry %p "
326
                             "which has been deallocated "
327
                             "without being cancelled",
328
                             GET_ENTRY(removed_node)));
329
#endif
330
#if ASSERT_IF_ENTRY_DESTROYED
331
        pj_assert(removed_node->dup._timer_id==removed_node->entry->_timer_id);
332
#endif
333
0
    } else {
334
0
        GET_ENTRY(removed_node)->_timer_id = -1;
335
0
    }
336
0
    GET_FIELD(removed_node, _timer_id) = -1;
337
338
0
#if !PJ_TIMER_USE_LINKED_LIST
339
    // Only try to reheapify if we're not deleting the last entry.
340
341
0
    if (slot < ht->cur_size)
342
0
    {
343
0
        pj_size_t parent;
344
0
        pj_timer_entry_dup *moved_node = ht->heap[ht->cur_size];
345
346
        // Move the end node to the location being removed and update
347
        // the corresponding slot in the parallel <timer_ids> array.
348
0
        copy_node( ht, slot, moved_node);
349
350
        // If the <moved_node->time_value_> is great than or equal its
351
        // parent it needs be moved down the heap.
352
0
        parent = HEAP_PARENT (slot);
353
354
0
        if (PJ_TIME_VAL_GTE(moved_node->_timer_value,
355
0
                            ht->heap[parent]->_timer_value))
356
0
        {
357
0
            reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
358
0
        } else {
359
0
            reheap_up( ht, moved_node, slot, parent);
360
0
        }
361
0
    }
362
#else
363
    pj_list_erase(removed_node);
364
#endif
365
366
0
    return removed_node;
367
0
}
368
369
static pj_status_t grow_heap(pj_timer_heap_t *ht)
370
0
{
371
    // All the containers will double in size from max_size_
372
0
    pj_size_t new_size = ht->max_size * 2;
373
0
#if PJ_TIMER_USE_COPY
374
0
    pj_timer_entry_dup *new_timer_dups = 0;
375
0
#endif
376
0
    pj_timer_id_t *new_timer_ids;
377
0
    pj_size_t i;
378
0
    pj_timer_entry_dup **new_heap = 0;
379
380
#if PJ_TIMER_USE_LINKED_LIST
381
    pj_timer_entry_dup *tmp_dup = NULL;
382
    pj_timer_entry_dup *new_dup;
383
#endif
384
385
0
    PJ_LOG(6,(THIS_FILE, "Growing heap size from %lu to %lu",
386
0
                         (unsigned long)ht->max_size,
387
0
                         (unsigned long)new_size));
388
389
    // First grow the heap itself.
390
0
    new_heap = (pj_timer_entry_dup**)
391
0
               pj_pool_calloc(ht->pool, (unsigned)new_size,
392
0
                              sizeof(pj_timer_entry_dup*));
393
0
    if (!new_heap)
394
0
        return PJ_ENOMEM;
395
396
0
#if PJ_TIMER_USE_COPY
397
    // Grow the array of timer copies.
398
399
0
    new_timer_dups = (pj_timer_entry_dup*)
400
0
                     pj_pool_alloc(ht->pool,
401
0
                                   sizeof(pj_timer_entry_dup) * new_size);
402
0
    if (!new_timer_dups)
403
0
        return PJ_ENOMEM;
404
405
0
    memcpy(new_timer_dups, ht->timer_dups,
406
0
           ht->max_size * sizeof(pj_timer_entry_dup));
407
0
    for (i = 0; i < ht->cur_size; i++) {
408
0
        int idx = (int)(ht->heap[i] - ht->timer_dups);
409
        // Point to the address in the new array
410
0
        pj_assert(idx >= 0 && idx < (int)ht->max_size);
411
0
        new_heap[i] = &new_timer_dups[idx];
412
0
    }
413
0
    ht->timer_dups = new_timer_dups;
414
#else
415
    memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry *));
416
#endif
417
418
#if PJ_TIMER_USE_LINKED_LIST
419
    tmp_dup = ht->head_list.next;
420
    pj_list_init(&ht->head_list);
421
    for (; tmp_dup != &ht->head_list; tmp_dup = tmp_dup->next)
422
    {
423
        int slot = ht->timer_ids[GET_FIELD(tmp_dup, _timer_id)];
424
        new_dup = new_heap[slot];
425
        pj_list_push_back(&ht->head_list, new_dup);
426
    }
427
#endif
428
429
0
    ht->heap = new_heap;
430
431
    // Grow the array of timer ids.
432
433
0
    new_timer_ids = 0;
434
0
    new_timer_ids = (pj_timer_id_t*)
435
0
                    pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
436
0
    if (!new_timer_ids)
437
0
        return PJ_ENOMEM;
438
439
0
    memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
440
441
    //delete [] timer_ids_;
442
0
    ht->timer_ids = new_timer_ids;
443
444
    // And add the new elements to the end of the "freelist".
445
0
    for (i = ht->max_size; i < new_size; i++)
446
0
        ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
447
448
0
    ht->max_size = new_size;
449
450
0
    return PJ_SUCCESS;
451
0
}
452
453
static pj_status_t insert_node(pj_timer_heap_t *ht,
454
                               pj_timer_entry *new_node,
455
                               const pj_time_val *future_time)
456
0
{
457
0
    pj_timer_entry_dup *timer_copy;
458
459
#if PJ_TIMER_USE_LINKED_LIST
460
    pj_timer_entry_dup *tmp_node = NULL;
461
#endif
462
463
0
    if (ht->cur_size + 2 >= ht->max_size) {
464
0
        pj_status_t status = grow_heap(ht);
465
0
        if (status != PJ_SUCCESS)
466
0
            return status;
467
0
    }
468
469
0
    timer_copy = GET_TIMER(ht, new_node);
470
0
#if PJ_TIMER_USE_COPY
471
    // Create a duplicate of the timer entry.
472
0
    pj_bzero(timer_copy, sizeof(*timer_copy));
473
0
    pj_memcpy(&timer_copy->dup, new_node, sizeof(*new_node));
474
0
    timer_copy->entry = new_node;
475
0
#endif
476
477
#if PJ_TIMER_USE_LINKED_LIST
478
    pj_list_init(timer_copy);
479
#endif
480
481
0
    timer_copy->_timer_value = *future_time;
482
483
0
#if !PJ_TIMER_USE_LINKED_LIST
484
0
    reheap_up(ht, timer_copy, ht->cur_size, HEAP_PARENT(ht->cur_size));
485
#else
486
    if (ht->cur_size == 0) {
487
        pj_list_push_back(&ht->head_list, timer_copy);
488
    } else if (PJ_TIME_VAL_GTE(*future_time,
489
                               ht->head_list.prev->_timer_value))
490
    {
491
        /* Insert the max value to the end of the list. */
492
        pj_list_insert_before(&ht->head_list, timer_copy);
493
    } else {
494
        tmp_node = ht->head_list.next;
495
        while (tmp_node->next != &ht->head_list &&
496
               PJ_TIME_VAL_GT(*future_time, tmp_node->_timer_value))
497
        {
498
            tmp_node = tmp_node->next;
499
        }
500
        if (PJ_TIME_VAL_LT(*future_time, tmp_node->_timer_value)) {
501
            pj_list_insert_before(tmp_node, timer_copy);
502
        } else {
503
            pj_list_insert_after(tmp_node, timer_copy);
504
        }
505
    }
506
    copy_node(ht, new_node->_timer_id-1, timer_copy);
507
#endif
508
0
    ht->cur_size++;
509
510
0
    return PJ_SUCCESS;
511
0
}
512
513
514
static pj_status_t schedule_entry( pj_timer_heap_t *ht,
515
                                   pj_timer_entry *entry, 
516
                                   const pj_time_val *future_time )
517
0
{
518
0
    if (ht->cur_size < ht->max_size)
519
0
    {
520
        // Obtain the next unique sequence number.
521
        // Set the entry
522
0
        entry->_timer_id = pop_freelist(ht);
523
524
0
        return insert_node( ht, entry, future_time );
525
0
    }
526
0
    else
527
0
        return -1;
528
0
}
529
530
531
static int cancel( pj_timer_heap_t *ht, 
532
                   pj_timer_entry *entry, 
533
                   unsigned flags)
534
0
{
535
0
    long timer_node_slot;
536
537
0
    PJ_CHECK_STACK();
538
539
    // Check to see if the timer_id is out of range.
540
    // Moved to cancel_timer() as it needs to validate _timer_id earlier
541
    /*
542
    if (entry->_timer_id < 1 || (pj_size_t)entry->_timer_id >= ht->max_size) {
543
        entry->_timer_id = -1;
544
        return 0;
545
    }
546
    */
547
548
0
    timer_node_slot = ht->timer_ids[entry->_timer_id];
549
550
0
    if (timer_node_slot < 0) { // Check to see if timer_id is still valid.
551
0
        entry->_timer_id = -1;
552
0
        return 0;
553
0
    }
554
555
0
    if (entry != GET_ENTRY(ht->heap[timer_node_slot])) {
556
0
        if ((flags & F_DONT_ASSERT) == 0)
557
0
            pj_assert(entry == GET_ENTRY(ht->heap[timer_node_slot]));
558
0
        entry->_timer_id = -1;
559
0
        return 0;
560
0
    } else {
561
0
        remove_node( ht, timer_node_slot);
562
563
0
        if ((flags & F_DONT_CALL) == 0) {
564
            // Call the close hook.
565
0
            (*ht->callback)(ht, entry);
566
0
        }
567
0
        return 1;
568
0
    }
569
0
}
570
571
572
/*
573
 * Calculate memory size required to create a timer heap.
574
 */
575
PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
576
0
{
577
0
    return /* size of the timer heap itself: */
578
0
           sizeof(pj_timer_heap_t) + 
579
           /* size of each entry: */
580
0
           (count+2) * (sizeof(pj_timer_entry_dup*)+sizeof(pj_timer_id_t)+
581
0
           sizeof(pj_timer_entry_dup)) +
582
           /* lock, pool etc: */
583
0
           132;
584
0
}
585
586
/*
587
 * Create a new timer heap.
588
 */
589
PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
590
                                          pj_size_t size,
591
                                          pj_timer_heap_t **p_heap)
592
5.01k
{
593
5.01k
    pj_timer_heap_t *ht;
594
5.01k
    pj_size_t i;
595
596
5.01k
    PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
597
598
5.01k
    *p_heap = NULL;
599
600
    /* Magic? */
601
5.01k
    size += 2;
602
603
    /* Allocate timer heap data structure from the pool */
604
5.01k
    ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
605
5.01k
    if (!ht)
606
0
        return PJ_ENOMEM;
607
608
    /* Initialize timer heap sizes */
609
5.01k
    ht->max_size = size;
610
5.01k
    ht->cur_size = 0;
611
5.01k
    ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
612
5.01k
    ht->timer_ids_freelist = 1;
613
5.01k
    ht->pool = pool;
614
615
    /* Lock. */
616
5.01k
    ht->lock = NULL;
617
5.01k
    ht->auto_delete_lock = 0;
618
619
    // Create the heap array.
620
5.01k
    ht->heap = (pj_timer_entry_dup**)
621
5.01k
               pj_pool_calloc(pool, (unsigned)size,
622
5.01k
                              sizeof(pj_timer_entry_dup*));
623
5.01k
    if (!ht->heap)
624
0
        return PJ_ENOMEM;
625
626
5.01k
#if PJ_TIMER_USE_COPY
627
    // Create the timer entry copies array.
628
5.01k
    ht->timer_dups = (pj_timer_entry_dup*)
629
5.01k
                     pj_pool_alloc(pool, sizeof(pj_timer_entry_dup) * size);
630
5.01k
    if (!ht->timer_dups)
631
0
        return PJ_ENOMEM;
632
5.01k
#endif
633
634
    // Create the parallel
635
5.01k
    ht->timer_ids = (pj_timer_id_t *)
636
5.01k
                    pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
637
5.01k
    if (!ht->timer_ids)
638
0
        return PJ_ENOMEM;
639
640
    // Initialize the "freelist," which uses negative values to
641
    // distinguish freelist elements from "pointers" into the <heap_>
642
    // array.
643
15.3M
    for (i=0; i<size; ++i)
644
15.3M
        ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
645
646
#if PJ_TIMER_USE_LINKED_LIST
647
    pj_list_init(&ht->head_list);
648
#endif
649
650
5.01k
    *p_heap = ht;
651
5.01k
    return PJ_SUCCESS;
652
5.01k
}
653
654
PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
655
5.01k
{
656
5.01k
    if (ht->lock && ht->auto_delete_lock) {
657
5.01k
        pj_lock_destroy(ht->lock);
658
5.01k
        ht->lock = NULL;
659
5.01k
    }
660
5.01k
}
661
662
PJ_DEF(void) pj_timer_heap_set_lock(  pj_timer_heap_t *ht,
663
                                      pj_lock_t *lock,
664
                                      pj_bool_t auto_del )
665
5.01k
{
666
5.01k
    if (ht->lock && ht->auto_delete_lock)
667
0
        pj_lock_destroy(ht->lock);
668
669
5.01k
    ht->lock = lock;
670
5.01k
    ht->auto_delete_lock = auto_del;
671
5.01k
}
672
673
674
PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
675
                                                          unsigned count )
676
5.01k
{
677
5.01k
    unsigned old_count = ht->max_entries_per_poll;
678
5.01k
    ht->max_entries_per_poll = count;
679
5.01k
    return old_count;
680
5.01k
}
681
682
PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
683
                                             int id,
684
                                             void *user_data,
685
                                             pj_timer_heap_callback *cb )
686
0
{
687
0
    pj_assert(entry && cb);
688
689
0
    entry->_timer_id = -1;
690
0
    entry->id = id;
691
0
    entry->user_data = user_data;
692
0
    entry->cb = cb;
693
#if !PJ_TIMER_USE_COPY
694
    entry->_grp_lock = NULL;
695
#endif
696
697
0
    return entry;
698
0
}
699
700
PJ_DEF(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry )
701
0
{
702
0
    return (entry->_timer_id >= 1);
703
0
}
704
705
#if PJ_TIMER_DEBUG
706
static pj_status_t schedule_w_grp_lock_dbg(pj_timer_heap_t *ht,
707
                                           pj_timer_entry *entry,
708
                                           const pj_time_val *delay,
709
                                           pj_bool_t set_id,
710
                                           int id_val,
711
                                           pj_grp_lock_t *grp_lock,
712
                                           const char *src_file,
713
                                           int src_line)
714
#else
715
static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,
716
                                       pj_timer_entry *entry,
717
                                       const pj_time_val *delay,
718
                                       pj_bool_t set_id,
719
                                       int id_val,
720
                                       pj_grp_lock_t *grp_lock)
721
#endif
722
0
{
723
0
    pj_status_t status;
724
0
    pj_time_val expires;
725
726
0
    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
727
0
    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
728
729
    /* Prevent same entry from being scheduled more than once */
730
    //PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
731
732
0
    pj_gettickcount(&expires);
733
0
    PJ_TIME_VAL_ADD(expires, *delay);
734
735
0
    lock_timer_heap(ht);
736
737
    /* Prevent same entry from being scheduled more than once */
738
0
    if (pj_timer_entry_running(entry)) {
739
0
        unlock_timer_heap(ht);
740
0
        PJ_LOG(3,(THIS_FILE, "Warning! Rescheduling outstanding entry (%p)",
741
0
                  entry));
742
0
        return PJ_EINVALIDOP;
743
0
    }
744
745
0
    status = schedule_entry(ht, entry, &expires);
746
0
    if (status == PJ_SUCCESS) {
747
0
        pj_timer_entry_dup *timer_copy = GET_TIMER(ht, entry);
748
749
0
        if (set_id)
750
0
            GET_FIELD(timer_copy, id) = entry->id = id_val;
751
0
        timer_copy->_grp_lock = grp_lock;
752
0
        if (timer_copy->_grp_lock) {
753
0
            pj_grp_lock_add_ref(timer_copy->_grp_lock);
754
0
        }
755
0
#if PJ_TIMER_DEBUG
756
0
        timer_copy->src_file = src_file;
757
0
        timer_copy->src_line = src_line;
758
0
#endif
759
0
    }
760
0
    unlock_timer_heap(ht);
761
762
0
    return status;
763
0
}
764
765
766
#if PJ_TIMER_DEBUG
767
PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht,
768
                                                pj_timer_entry *entry,
769
                                                const pj_time_val *delay,
770
                                                const char *src_file,
771
                                                int src_line)
772
0
{
773
0
    return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_FALSE, 1, NULL,
774
0
                                   src_file, src_line);
775
0
}
776
777
PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
778
                                                pj_timer_heap_t *ht,
779
                                                pj_timer_entry *entry,
780
                                                const pj_time_val *delay,
781
                                                int id_val,
782
                                                pj_grp_lock_t *grp_lock,
783
                                                const char *src_file,
784
                                                int src_line)
785
0
{
786
0
    return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_TRUE, id_val,
787
0
                                   grp_lock, src_file, src_line);
788
0
}
789
790
#else
791
PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
792
                                            pj_timer_entry *entry,
793
                                            const pj_time_val *delay)
794
{
795
    return schedule_w_grp_lock(ht, entry, delay, PJ_FALSE, 1, NULL);
796
}
797
798
PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
799
                                                      pj_timer_entry *entry,
800
                                                      const pj_time_val *delay,
801
                                                      int id_val,
802
                                                      pj_grp_lock_t *grp_lock)
803
{
804
    return schedule_w_grp_lock(ht, entry, delay, PJ_TRUE, id_val, grp_lock);
805
}
806
#endif
807
808
static int cancel_timer(pj_timer_heap_t *ht,
809
                        pj_timer_entry *entry,
810
                        unsigned flags,
811
                        int id_val)
812
0
{
813
0
    pj_timer_entry_dup *timer_copy;
814
0
    pj_grp_lock_t *grp_lock;
815
0
    int count;
816
817
0
    PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
818
819
0
    lock_timer_heap(ht);
820
821
    // Check to see if the timer_id is out of range
822
0
    if (entry->_timer_id < 1 || (pj_size_t)entry->_timer_id >= ht->max_size) {
823
0
        unlock_timer_heap(ht);
824
0
        return 0;
825
0
    }
826
827
0
    timer_copy = GET_TIMER(ht, entry);
828
0
    grp_lock = timer_copy->_grp_lock;
829
830
0
    count = cancel(ht, entry, flags | F_DONT_CALL);
831
0
    if (count > 0) {
832
        /* Timer entry found & cancelled */
833
0
        if (flags & F_SET_ID) {
834
0
            entry->id = id_val;
835
0
        }
836
0
        if (grp_lock) {
837
0
            pj_grp_lock_dec_ref(grp_lock);
838
0
        }
839
0
    }
840
0
    unlock_timer_heap(ht);
841
842
0
    return count;
843
0
}
844
845
PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
846
                                  pj_timer_entry *entry)
847
0
{
848
0
    return cancel_timer(ht, entry, 0, 0);
849
0
}
850
851
PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
852
                                           pj_timer_entry *entry,
853
                                           int id_val)
854
0
{
855
0
    return cancel_timer(ht, entry, F_SET_ID | F_DONT_ASSERT, id_val);
856
0
}
857
858
PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
859
                                     pj_time_val *next_delay )
860
0
{
861
0
    pj_time_val now;
862
0
    pj_time_val min_time_node = {0,0};
863
0
    unsigned count;
864
0
    pj_timer_id_t slot = 0;
865
866
0
    PJ_ASSERT_RETURN(ht, 0);
867
868
0
    lock_timer_heap(ht);
869
0
    if (!ht->cur_size && next_delay) {
870
0
        next_delay->sec = next_delay->msec = PJ_MAXINT32;
871
0
        unlock_timer_heap(ht);
872
0
        return 0;
873
0
    }
874
875
0
    count = 0;
876
0
    pj_gettickcount(&now);
877
878
0
    if (ht->cur_size) {
879
#if PJ_TIMER_USE_LINKED_LIST
880
        slot = ht->timer_ids[GET_FIELD(ht->head_list.next, _timer_id)];
881
#endif
882
0
        min_time_node = ht->heap[slot]->_timer_value;
883
0
    }
884
885
0
    while ( ht->cur_size && 
886
0
            PJ_TIME_VAL_LTE(min_time_node, now) &&
887
0
            count < ht->max_entries_per_poll ) 
888
0
    {
889
0
        pj_timer_entry_dup *node = remove_node(ht, slot);
890
0
        pj_timer_entry *entry = GET_ENTRY(node);
891
        /* Avoid re-use of this timer until the callback is done. */
892
        ///Not necessary, even causes problem (see also #2176).
893
        ///pj_timer_id_t node_timer_id = pop_freelist(ht);
894
0
        pj_grp_lock_t *grp_lock;
895
0
        pj_bool_t valid = PJ_TRUE;
896
897
0
        ++count;
898
899
0
        grp_lock = node->_grp_lock;
900
0
        node->_grp_lock = NULL;
901
0
        if (GET_FIELD(node, cb) != entry->cb ||
902
0
            GET_FIELD(node, user_data) != entry->user_data)
903
0
        {
904
0
            valid = PJ_FALSE;
905
0
#if PJ_TIMER_DEBUG
906
0
            PJ_LOG(3,(THIS_FILE, "Bug! Polling entry %p from %s line %d has "
907
0
                                 "been deallocated without being cancelled",
908
0
                                 GET_ENTRY(node),
909
0
                                 node->src_file, node->src_line));
910
#else
911
            PJ_LOG(3,(THIS_FILE, "Bug! Polling entry %p has "
912
                                 "been deallocated without being cancelled",
913
                                 GET_ENTRY(node)));
914
#endif
915
#if ASSERT_IF_ENTRY_DESTROYED
916
            pj_assert(node->dup.cb == entry->cb);
917
            pj_assert(node->dup.user_data == entry->user_data);
918
#endif
919
0
        }
920
921
0
        unlock_timer_heap(ht);
922
923
0
        PJ_RACE_ME(5);
924
925
0
        if (valid && entry->cb)
926
0
            (*entry->cb)(ht, entry);
927
928
0
        if (valid && grp_lock)
929
0
            pj_grp_lock_dec_ref(grp_lock);
930
931
0
        lock_timer_heap(ht);
932
        /* Now, the timer is really free for re-use. */
933
        ///push_freelist(ht, node_timer_id);
934
935
0
        if (ht->cur_size) {
936
#if PJ_TIMER_USE_LINKED_LIST
937
            slot = ht->timer_ids[GET_FIELD(ht->head_list.next, _timer_id)];
938
#endif
939
0
            min_time_node = ht->heap[slot]->_timer_value;
940
            /* Update now */
941
0
            pj_gettickcount(&now);
942
0
        }
943
0
    }
944
0
    if (ht->cur_size && next_delay) {
945
0
        *next_delay = ht->heap[0]->_timer_value;
946
0
        if (count > 0)
947
0
            pj_gettickcount(&now);
948
0
        PJ_TIME_VAL_SUB(*next_delay, now);
949
0
        if (next_delay->sec < 0 || next_delay->msec < 0)
950
0
            next_delay->sec = next_delay->msec = 0;
951
0
    } else if (next_delay) {
952
0
        next_delay->sec = next_delay->msec = PJ_MAXINT32;
953
0
    }
954
0
    unlock_timer_heap(ht);
955
956
0
    return count;
957
0
}
958
959
PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
960
0
{
961
0
    PJ_ASSERT_RETURN(ht, 0);
962
963
0
    return ht->cur_size;
964
0
}
965
966
PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
967
                                                 pj_time_val *timeval)
968
0
{
969
0
    pj_assert(ht->cur_size != 0);
970
0
    if (ht->cur_size == 0)
971
0
        return PJ_ENOTFOUND;
972
973
0
    lock_timer_heap(ht);
974
0
    *timeval = ht->heap[0]->_timer_value;
975
0
    unlock_timer_heap(ht);
976
977
0
    return PJ_SUCCESS;
978
0
}
979
980
#if PJ_TIMER_DEBUG
981
PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht)
982
5.01k
{
983
5.01k
    lock_timer_heap(ht);
984
985
5.01k
    PJ_LOG(3,(THIS_FILE, "Dumping timer heap:"));
986
5.01k
    PJ_LOG(3,(THIS_FILE, "  Cur size: %d entries, max: %d",
987
5.01k
                         (int)ht->cur_size, (int)ht->max_size));
988
989
5.01k
    if (ht->cur_size) {
990
#if PJ_TIMER_USE_LINKED_LIST
991
        pj_timer_entry_dup *tmp_dup;
992
#else
993
0
        unsigned i;
994
0
#endif
995
0
        pj_time_val now;
996
997
0
        PJ_LOG(3,(THIS_FILE, "  Entries: "));
998
0
        PJ_LOG(3,(THIS_FILE, "    _id\tId\tElapsed\tSource"));
999
0
        PJ_LOG(3,(THIS_FILE, "    ----------------------------------"));
1000
1001
0
        pj_gettickcount(&now);
1002
1003
0
#if !PJ_TIMER_USE_LINKED_LIST
1004
0
        for (i=0; i<(unsigned)ht->cur_size; ++i)
1005
0
        {
1006
0
            pj_timer_entry_dup *e = ht->heap[i];
1007
#else
1008
        for (tmp_dup = ht->head_list.next; tmp_dup != &ht->head_list;
1009
             tmp_dup = tmp_dup->next)
1010
        {
1011
            pj_timer_entry_dup *e = tmp_dup;
1012
#endif
1013
1014
0
            pj_time_val delta;
1015
1016
0
            if (PJ_TIME_VAL_LTE(e->_timer_value, now))
1017
0
                delta.sec = delta.msec = 0;
1018
0
            else {
1019
0
                delta = e->_timer_value;
1020
0
                PJ_TIME_VAL_SUB(delta, now);
1021
0
            }
1022
1023
0
            PJ_LOG(3,(THIS_FILE, "    %d\t%d\t%d.%03d\t%s:%d",
1024
0
                      GET_FIELD(e, _timer_id), GET_FIELD(e, id),
1025
0
                      (int)delta.sec, (int)delta.msec,
1026
0
                      e->src_file, e->src_line));
1027
0
        }
1028
0
    }
1029
1030
5.01k
    unlock_timer_heap(ht);
1031
5.01k
}
1032
#endif
1033