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