/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 | | |