<p>Kevin Harwell <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14741">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  George Joseph: Looks good to me, approved
  Kevin Harwell: Approved for Submit

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pjsip: Add timer refactor patch and timer 0 cancellation.<br><br>There have been numerous timer issues over the years which<br>resulted in Teluu doing a major refactor of the timer<br>implementation whereby the timer entries themselves are not<br>trusted. This change backports this refactor which has<br>been shown to resolve timer crashes.<br><br>Additionally another patch has been backported to prevent<br>timer entries with an internal id of 0 from being canceled.<br>This would result in the invalid timer id of 0 being placed<br>into the timer heap causing issues. This is also a backport.<br><br>Change-Id: I1ba318b1f153a6dd7458846396e2867282b428e7<br>(cherry picked from commit 9ce6d46aea3a7ec70fe923a58cb2aafaa1ea64a8)<br>---<br>A third-party/pjproject/patches/0040-pjsip-timer-refactor.patch<br>A third-party/pjproject/patches/0041-pjlib_cancel_timer_0.patch<br>2 files changed, 1,187 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/third-party/pjproject/patches/0040-pjsip-timer-refactor.patch b/third-party/pjproject/patches/0040-pjsip-timer-refactor.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..18b6be4</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0040-pjsip-timer-refactor.patch</span><br><span>@@ -0,0 +1,1148 @@</span><br><span style="color: hsl(120, 100%, 40%);">+commit a49822da70a42b6c14680f84281d1ea94b6894f5</span><br><span style="color: hsl(120, 100%, 40%);">+Author: Sauw Ming <ming@teluu.com></span><br><span style="color: hsl(120, 100%, 40%);">+Date:   Tue Sep 3 02:10:45 2019 +0000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    Fixed #2225: Timer heap refactoring</span><br><span style="color: hsl(120, 100%, 40%);">+    </span><br><span style="color: hsl(120, 100%, 40%);">+    </span><br><span style="color: hsl(120, 100%, 40%);">+    </span><br><span style="color: hsl(120, 100%, 40%);">+    git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@6058 74dad513-b988-da41-8d7b-12977e46ad98</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h</span><br><span style="color: hsl(120, 100%, 40%);">+index 4df9d923f..deb1d7594 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/include/pj/config.h</span><br><span>++++ b/pjlib/include/pj/config.h</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -531,16 +531,47 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ #endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++/**</span><br><span style="color: hsl(120, 100%, 40%);">++ * If enabled, when calling pj_pool_release(), the memory pool content</span><br><span style="color: hsl(120, 100%, 40%);">++ * will be wiped out first before released.</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * Default: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++#ifndef PJ_POOL_RELEASE_WIPE_DATA</span><br><span style="color: hsl(120, 100%, 40%);">++#  define PJ_POOL_RELEASE_WIPE_DATA   0</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ /**</span><br><span style="color: hsl(120, 100%, 40%);">+  * Enable timer heap debugging facility. When this is enabled, application</span><br><span style="color: hsl(120, 100%, 40%);">+  * can call pj_timer_heap_dump() to show the contents of the timer heap</span><br><span style="color: hsl(120, 100%, 40%);">+  * along with the source location where the timer entries were scheduled.</span><br><span style="color: hsl(120, 100%, 40%);">+  * See https://trac.pjsip.org/repos/ticket/1527 for more info.</span><br><span style="color: hsl(120, 100%, 40%);">+  *</span><br><span style="color: hsl(120, 100%, 40%);">+- * Default: 0</span><br><span style="color: hsl(120, 100%, 40%);">++ * Default: 1</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+ #ifndef PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">+-#  define PJ_TIMER_DEBUG           0</span><br><span style="color: hsl(120, 100%, 40%);">++#  define PJ_TIMER_DEBUG            1</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++/**</span><br><span style="color: hsl(120, 100%, 40%);">++ * If enabled, the timer heap will keep internal copies of the timer entries.</span><br><span style="color: hsl(120, 100%, 40%);">++ * This will increase the robustness and stability of the timer heap (against</span><br><span style="color: hsl(120, 100%, 40%);">++ * accidental modification or premature deallocation of the timer entries) and</span><br><span style="color: hsl(120, 100%, 40%);">++ * makes it easier to troubleshoot any timer related issues, with the overhead</span><br><span style="color: hsl(120, 100%, 40%);">++ * of additional memory space required.</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * Note that the detection against premature deallocation only works if the</span><br><span style="color: hsl(120, 100%, 40%);">++ * freed memory content has changed (such as if it's been reallocated and</span><br><span style="color: hsl(120, 100%, 40%);">++ * overwritten by another data. Alternatively, you can enable</span><br><span style="color: hsl(120, 100%, 40%);">++ * PJ_POOL_RELEASE_WIPE_DATA which will erase the data first before releasing</span><br><span style="color: hsl(120, 100%, 40%);">++ * the memory).</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * Default: 1 (enabled)</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++#ifndef PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">++#  define PJ_TIMER_HEAP_USE_COPY    1</span><br><span style="color: hsl(120, 100%, 40%);">+ #endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/include/pj/pool_i.h b/pjlib/include/pj/pool_i.h</span><br><span style="color: hsl(120, 100%, 40%);">+index 52e777dc9..60edb7dd3 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/include/pj/pool_i.h</span><br><span>++++ b/pjlib/include/pj/pool_i.h</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -88,6 +88,17 @@ PJ_IDEF(pj_pool_t*) pj_pool_create( pj_pool_factory *f,</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ PJ_IDEF(void) pj_pool_release( pj_pool_t *pool )</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_POOL_RELEASE_WIPE_DATA</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_pool_block *b;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    b = pool->block_list.next;</span><br><span style="color: hsl(120, 100%, 40%);">++    while (b != &pool->block_list) {</span><br><span style="color: hsl(120, 100%, 40%);">++       volatile unsigned char *p = b->buf;</span><br><span style="color: hsl(120, 100%, 40%);">++       while (p < b->end) *p++ = 0;</span><br><span style="color: hsl(120, 100%, 40%);">++   b = b->next;</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     if (pool->factory->release_pool)</span><br><span style="color: hsl(120, 100%, 40%);">+      (*pool->factory->release_pool)(pool->factory, pool);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h</span><br><span style="color: hsl(120, 100%, 40%);">+index 14857b872..8adecd885 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/include/pj/timer.h</span><br><span>++++ b/pjlib/include/pj/timer.h</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -113,6 +113,7 @@ typedef struct pj_timer_entry</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_id_t _timer_id;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++#if !PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">+     /** </span><br><span style="color: hsl(120, 100%, 40%);">+      * The future time when the timer expires, which the value is updated</span><br><span style="color: hsl(120, 100%, 40%);">+      * by timer heap when the timer is scheduled.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -129,6 +130,8 @@ typedef struct pj_timer_entry</span><br><span style="color: hsl(120, 100%, 40%);">+     const char    *src_file;</span><br><span style="color: hsl(120, 100%, 40%);">+     int             src_line;</span><br><span style="color: hsl(120, 100%, 40%);">+ #endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ } pj_timer_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+index cbdd9791f..ead6d6d2f 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/src/pj/timer.c</span><br><span>++++ b/pjlib/src/pj/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -46,6 +46,15 @@</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ #define DEFAULT_MAX_TIMED_OUT_PER_POLL  (64)</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++/* Enable this to raise assertion in order to catch bug of timer entry</span><br><span style="color: hsl(120, 100%, 40%);">++ * which has been deallocated without being cancelled. If disabled,</span><br><span style="color: hsl(120, 100%, 40%);">++ * the timer heap will simply remove the destroyed entry (and print log)</span><br><span style="color: hsl(120, 100%, 40%);">++ * and resume normally.</span><br><span style="color: hsl(120, 100%, 40%);">++ * This setting only works if PJ_TIMER_HEAP_USE_COPY is enabled.</span><br><span style="color: hsl(120, 100%, 40%);">++ */</span><br><span style="color: hsl(120, 100%, 40%);">++#define ASSERT_IF_ENTRY_DESTROYED (PJ_TIMER_HEAP_USE_COPY? 0: 0)</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ enum</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     F_DONT_CALL = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -53,6 +62,53 @@ enum</span><br><span style="color: hsl(120, 100%, 40%);">+     F_SET_ID = 4</span><br><span style="color: hsl(120, 100%, 40%);">+ };</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++/* Duplicate/copy of the timer entry. */</span><br><span style="color: hsl(120, 100%, 40%);">++typedef struct pj_timer_entry_dup</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    /**</span><br><span style="color: hsl(120, 100%, 40%);">++     * The duplicate copy.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry  dup;            </span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++    /**</span><br><span style="color: hsl(120, 100%, 40%);">++     * Pointer of the original timer entry.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry *entry;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    /** </span><br><span style="color: hsl(120, 100%, 40%);">++     * The future time when the timer expires, which the value is updated</span><br><span style="color: hsl(120, 100%, 40%);">++     * by timer heap when the timer is scheduled.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_time_val     _timer_value;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    /**</span><br><span style="color: hsl(120, 100%, 40%);">++     * Internal: the group lock used by this entry, set when</span><br><span style="color: hsl(120, 100%, 40%);">++     * pj_timer_heap_schedule_w_lock() is used.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_grp_lock_t  *_grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">++    const char       *src_file;</span><br><span style="color: hsl(120, 100%, 40%);">++    int             src_line;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++} pj_timer_entry_dup;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_TIMER(ht, node) &ht->timer_dups[node->_timer_id]</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_ENTRY(node) node->entry</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_FIELD(node, _timer_id) node->dup._timer_id</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#else</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++typedef pj_timer_entry pj_timer_entry_dup;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_TIMER(ht, node) node</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_ENTRY(node) node</span><br><span style="color: hsl(120, 100%, 40%);">++#define GET_FIELD(node, _timer_id) node->_timer_id</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /**</span><br><span style="color: hsl(120, 100%, 40%);">+  * The implementation of timer heap.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -83,7 +139,7 @@ struct pj_timer_heap_t</span><br><span style="color: hsl(120, 100%, 40%);">+      * ordered, almost complete" binary tree, which is stored in an</span><br><span style="color: hsl(120, 100%, 40%);">+      * array.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_timer_entry **heap;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup **heap;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /**</span><br><span style="color: hsl(120, 100%, 40%);">+      * An array of "pointers" that allows each pj_timer_entry in the</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -96,6 +152,11 @@ struct pj_timer_heap_t</span><br><span style="color: hsl(120, 100%, 40%);">+      * values are treated as "pointers" into the <heap_> array.</span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_id_t *timer_ids;</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++    /**</span><br><span style="color: hsl(120, 100%, 40%);">++     * An array of timer entry copies.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup *timer_dups;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /**</span><br><span style="color: hsl(120, 100%, 40%);">+      * "Pointer" to the first element in the freelist contained within</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -126,7 +187,7 @@ PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ static void copy_node( pj_timer_heap_t *ht, pj_size_t slot, </span><br><span style="color: hsl(120, 100%, 40%);">+-                     pj_timer_entry *moved_node )</span><br><span style="color: hsl(120, 100%, 40%);">++                 pj_timer_entry_dup *moved_node )</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_CHECK_STACK();</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -134,7 +195,7 @@ static void copy_node( pj_timer_heap_t *ht, pj_size_t slot,</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->heap[slot] = moved_node;</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     // Update the corresponding slot in the parallel <timer_ids_> array.</span><br><span style="color: hsl(120, 100%, 40%);">+-    ht->timer_ids[moved_node->_timer_id] = (int)slot;</span><br><span style="color: hsl(120, 100%, 40%);">++    ht->timer_ids[GET_FIELD(moved_node, _timer_id)] = (int)slot;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -164,7 +225,7 @@ static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">++static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+                         size_t slot, size_t child)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_CHECK_STACK();</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -174,13 +235,17 @@ static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+     while (child < ht->cur_size)</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+  // Choose the smaller of the two children.</span><br><span style="color: hsl(120, 100%, 40%);">+-   if (child + 1 < ht->cur_size</span><br><span style="color: hsl(120, 100%, 40%);">+-       && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++ if (child + 1 < ht->cur_size &&</span><br><span style="color: hsl(120, 100%, 40%);">++            PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value,</span><br><span style="color: hsl(120, 100%, 40%);">++                     ht->heap[child]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++     {</span><br><span style="color: hsl(120, 100%, 40%);">+         child++;</span><br><span style="color: hsl(120, 100%, 40%);">++ }</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+      // Perform a <copy> if the child has a larger timeout value than</span><br><span style="color: hsl(120, 100%, 40%);">+        // the <moved_node>.</span><br><span style="color: hsl(120, 100%, 40%);">+-   if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++        if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value,</span><br><span style="color: hsl(120, 100%, 40%);">++                         moved_node->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">+         {</span><br><span style="color: hsl(120, 100%, 40%);">+      copy_node( ht, slot, ht->heap[child]);</span><br><span style="color: hsl(120, 100%, 40%);">+             slot = child;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -194,7 +259,7 @@ static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+     copy_node( ht, slot, moved_node);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">++static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+                       size_t slot, size_t parent)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     // Restore the heap property after an insertion.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -203,7 +268,8 @@ static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+    // If the parent node is greater than the <moved_node> we need</span><br><span style="color: hsl(120, 100%, 40%);">+  // to copy it down.</span><br><span style="color: hsl(120, 100%, 40%);">+-  if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++       if (PJ_TIME_VAL_LT(moved_node->_timer_value,</span><br><span style="color: hsl(120, 100%, 40%);">++                         ht->heap[parent]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">+         {</span><br><span style="color: hsl(120, 100%, 40%);">+             copy_node(ht, slot, ht->heap[parent]);</span><br><span style="color: hsl(120, 100%, 40%);">+             slot = parent;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -219,26 +285,44 @@ static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)</span><br><span style="color: hsl(120, 100%, 40%);">++static pj_timer_entry_dup * remove_node( pj_timer_heap_t *ht, size_t slot)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_timer_entry *removed_node = ht->heap[slot];</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup *removed_node = ht->heap[slot];</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     // Return this timer id to the freelist.</span><br><span style="color: hsl(120, 100%, 40%);">+-    push_freelist( ht, removed_node->_timer_id );</span><br><span style="color: hsl(120, 100%, 40%);">++    push_freelist( ht, GET_FIELD(removed_node, _timer_id) );</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     // Decrement the size of the heap by one since we're removing the</span><br><span style="color: hsl(120, 100%, 40%);">+     // "slot"th node.</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->cur_size--;</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     // Set the ID</span><br><span style="color: hsl(120, 100%, 40%);">+-    removed_node->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">++    if (GET_FIELD(removed_node, _timer_id) !=</span><br><span style="color: hsl(120, 100%, 40%);">++        GET_ENTRY(removed_node)->_timer_id)</span><br><span style="color: hsl(120, 100%, 40%);">++    {</span><br><span style="color: hsl(120, 100%, 40%);">++           PJ_LOG(3,(THIS_FILE, "Bug! Trying to remove entry %p from %s "</span><br><span style="color: hsl(120, 100%, 40%);">++                          "line %d, which has been deallocated "</span><br><span style="color: hsl(120, 100%, 40%);">++                             "without being cancelled",</span><br><span style="color: hsl(120, 100%, 40%);">++                                 GET_ENTRY(removed_node),</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">++                                removed_node->src_file,</span><br><span style="color: hsl(120, 100%, 40%);">++                           removed_node->src_line));</span><br><span style="color: hsl(120, 100%, 40%);">++#else</span><br><span style="color: hsl(120, 100%, 40%);">++                                 "N/A", 0));</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++#if ASSERT_IF_ENTRY_DESTROYED</span><br><span style="color: hsl(120, 100%, 40%);">++      pj_assert(removed_node->dup._timer_id==removed_node->entry->_timer_id);</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    GET_ENTRY(removed_node)->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">++    GET_FIELD(removed_node, _timer_id) = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     // Only try to reheapify if we're not deleting the last entry.</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     if (slot < ht->cur_size)</span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+    pj_size_t parent;</span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_timer_entry *moved_node = ht->heap[ht->cur_size];</span><br><span style="color: hsl(120, 100%, 40%);">++   pj_timer_entry_dup *moved_node = ht->heap[ht->cur_size];</span><br><span style="color: hsl(120, 100%, 40%);">+        </span><br><span style="color: hsl(120, 100%, 40%);">+      // Move the end node to the location being removed and update</span><br><span style="color: hsl(120, 100%, 40%);">+         // the corresponding slot in the parallel <timer_ids> array.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -248,30 +332,59 @@ static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)</span><br><span style="color: hsl(120, 100%, 40%);">+      // parent it needs be moved down the heap.</span><br><span style="color: hsl(120, 100%, 40%);">+    parent = HEAP_PARENT (slot);</span><br><span style="color: hsl(120, 100%, 40%);">+  </span><br><span style="color: hsl(120, 100%, 40%);">+-     if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++      if (PJ_TIME_VAL_GTE(moved_node->_timer_value,</span><br><span style="color: hsl(120, 100%, 40%);">++                         ht->heap[parent]->_timer_value))</span><br><span style="color: hsl(120, 100%, 40%);">++   {</span><br><span style="color: hsl(120, 100%, 40%);">+         reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));</span><br><span style="color: hsl(120, 100%, 40%);">+-     else</span><br><span style="color: hsl(120, 100%, 40%);">++ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+          reheap_up( ht, moved_node, slot, parent);</span><br><span style="color: hsl(120, 100%, 40%);">++        }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     return removed_node;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-static void grow_heap(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">++static pj_status_t grow_heap(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     // All the containers will double in size from max_size_</span><br><span style="color: hsl(120, 100%, 40%);">+     size_t new_size = ht->max_size * 2;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup *new_timer_dups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_id_t *new_timer_ids;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_size_t i;</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(6,(THIS_FILE, "Growing heap size from %d to %d",</span><br><span style="color: hsl(120, 100%, 40%);">++                       ht->max_size, new_size));</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">+     // First grow the heap itself.</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_timer_entry **new_heap = 0;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup **new_heap = 0;</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++    new_heap = (pj_timer_entry_dup**) </span><br><span style="color: hsl(120, 100%, 40%);">++              pj_pool_calloc(ht->pool, new_size, sizeof(pj_timer_entry_dup*));</span><br><span style="color: hsl(120, 100%, 40%);">++    if (!new_heap)</span><br><span style="color: hsl(120, 100%, 40%);">++      return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">++    // Grow the array of timer copies.</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+-    new_heap = (pj_timer_entry**) </span><br><span style="color: hsl(120, 100%, 40%);">+-                  pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);</span><br><span style="color: hsl(120, 100%, 40%);">+-    memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));</span><br><span style="color: hsl(120, 100%, 40%);">+-    //delete [] this->heap_;</span><br><span style="color: hsl(120, 100%, 40%);">++    new_timer_dups = (pj_timer_entry_dup*) </span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_pool_alloc(ht->pool,</span><br><span style="color: hsl(120, 100%, 40%);">++                                 sizeof(pj_timer_entry_dup) * new_size);</span><br><span style="color: hsl(120, 100%, 40%);">++    if (!new_timer_dups)</span><br><span style="color: hsl(120, 100%, 40%);">++        return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    memcpy(new_timer_dups, ht->timer_dups,</span><br><span style="color: hsl(120, 100%, 40%);">++            ht->max_size * sizeof(pj_timer_entry_dup));</span><br><span style="color: hsl(120, 100%, 40%);">++    for (i = 0; i < ht->cur_size; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">++           int idx = ht->heap[i] - ht->timer_dups;</span><br><span style="color: hsl(120, 100%, 40%);">++        // Point to the address in the new array</span><br><span style="color: hsl(120, 100%, 40%);">++        pj_assert(idx >= 0 && idx < ht->max_size);</span><br><span style="color: hsl(120, 100%, 40%);">++       new_heap[i] = &new_timer_dups[idx];</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    ht->timer_dups = new_timer_dups;</span><br><span style="color: hsl(120, 100%, 40%);">++#else</span><br><span style="color: hsl(120, 100%, 40%);">++    memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry *));</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->heap = new_heap;</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     // Grow the array of timer ids.</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -279,7 +392,9 @@ static void grow_heap(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">+     new_timer_ids = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     new_timer_ids = (pj_timer_id_t*)</span><br><span style="color: hsl(120, 100%, 40%);">+                    pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));</span><br><span style="color: hsl(120, 100%, 40%);">+-    </span><br><span style="color: hsl(120, 100%, 40%);">++    if (!new_timer_ids)</span><br><span style="color: hsl(120, 100%, 40%);">++   return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     //delete [] timer_ids_;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -290,15 +405,35 @@ static void grow_heap(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">+  ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     ht->max_size = new_size;</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++    return PJ_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)</span><br><span style="color: hsl(120, 100%, 40%);">++static pj_status_t insert_node(pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">++                            pj_timer_entry *new_node,</span><br><span style="color: hsl(120, 100%, 40%);">++                            const pj_time_val *future_time)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+-    if (ht->cur_size + 2 >= ht->max_size)</span><br><span style="color: hsl(120, 100%, 40%);">+-      grow_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+-    </span><br><span style="color: hsl(120, 100%, 40%);">+-    reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup *timer_copy;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    if (ht->cur_size + 2 >= ht->max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">++ pj_status_t status = grow_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">++  if (status != PJ_SUCCESS)</span><br><span style="color: hsl(120, 100%, 40%);">++        return status;</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    timer_copy = GET_TIMER(ht, new_node);</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_HEAP_USE_COPY    </span><br><span style="color: hsl(120, 100%, 40%);">++    // Create a duplicate of the timer entry.</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_bzero(timer_copy, sizeof(*timer_copy));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_memcpy(&timer_copy->dup, new_node, sizeof(*new_node));</span><br><span style="color: hsl(120, 100%, 40%);">++    timer_copy->entry = new_node;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++    timer_copy->_timer_value = *future_time;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    reheap_up( ht, timer_copy, ht->cur_size, HEAP_PARENT(ht->cur_size));</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->cur_size++;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    return PJ_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -311,9 +446,8 @@ static pj_status_t schedule_entry( pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+  // Obtain the next unique sequence number.</span><br><span style="color: hsl(120, 100%, 40%);">+    // Set the entry</span><br><span style="color: hsl(120, 100%, 40%);">+      entry->_timer_id = pop_freelist(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+-      entry->_timer_value = *future_time;</span><br><span style="color: hsl(120, 100%, 40%);">+-       insert_node( ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+-     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++ return insert_node( ht, entry, future_time );</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     else</span><br><span style="color: hsl(120, 100%, 40%);">+      return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -324,38 +458,36 @@ static int cancel( pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+                pj_timer_entry *entry, </span><br><span style="color: hsl(120, 100%, 40%);">+               unsigned flags)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+-  long timer_node_slot;</span><br><span style="color: hsl(120, 100%, 40%);">++    long timer_node_slot;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-  PJ_CHECK_STACK();</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_CHECK_STACK();</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-  // Check to see if the timer_id is out of range</span><br><span style="color: hsl(120, 100%, 40%);">+-  if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+-    entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+-    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+-  }</span><br><span style="color: hsl(120, 100%, 40%);">++    // Check to see if the timer_id is out of range</span><br><span style="color: hsl(120, 100%, 40%);">++    if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">++ entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">++            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-  timer_node_slot = ht->timer_ids[entry->_timer_id];</span><br><span style="color: hsl(120, 100%, 40%);">++    timer_node_slot = ht->timer_ids[entry->_timer_id];</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-  if (timer_node_slot < 0) { // Check to see if timer_id is still valid.</span><br><span style="color: hsl(120, 100%, 40%);">+-    entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+-    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+-  }</span><br><span style="color: hsl(120, 100%, 40%);">+-</span><br><span style="color: hsl(120, 100%, 40%);">+-  if (entry != ht->heap[timer_node_slot])</span><br><span style="color: hsl(120, 100%, 40%);">+-    {</span><br><span style="color: hsl(120, 100%, 40%);">+-      if ((flags & F_DONT_ASSERT) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+-        pj_assert(entry == ht->heap[timer_node_slot]);</span><br><span style="color: hsl(120, 100%, 40%);">+-      entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+-      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">++    if (timer_node_slot < 0) { // Check to see if timer_id is still valid.</span><br><span style="color: hsl(120, 100%, 40%);">++      entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">++            return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+-  else</span><br><span style="color: hsl(120, 100%, 40%);">+-    {</span><br><span style="color: hsl(120, 100%, 40%);">+-      remove_node( ht, timer_node_slot);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-      if ((flags & F_DONT_CALL) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+-        // Call the close hook.</span><br><span style="color: hsl(120, 100%, 40%);">+-       (*ht->callback)(ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+-      return 1;</span><br><span style="color: hsl(120, 100%, 40%);">++    if (entry != GET_ENTRY(ht->heap[timer_node_slot])) {</span><br><span style="color: hsl(120, 100%, 40%);">++       if ((flags & F_DONT_ASSERT) == 0)</span><br><span style="color: hsl(120, 100%, 40%);">++            pj_assert(entry == GET_ENTRY(ht->heap[timer_node_slot]));</span><br><span style="color: hsl(120, 100%, 40%);">++        entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">++         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">++    } else {</span><br><span style="color: hsl(120, 100%, 40%);">++             remove_node( ht, timer_node_slot);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++        if ((flags & F_DONT_CALL) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">++            // Call the close hook.</span><br><span style="color: hsl(120, 100%, 40%);">++      (*ht->callback)(ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">++   }</span><br><span style="color: hsl(120, 100%, 40%);">++            return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -368,7 +500,8 @@ PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)</span><br><span style="color: hsl(120, 100%, 40%);">+     return /* size of the timer heap itself: */</span><br><span style="color: hsl(120, 100%, 40%);">+            sizeof(pj_timer_heap_t) + </span><br><span style="color: hsl(120, 100%, 40%);">+            /* size of each entry: */</span><br><span style="color: hsl(120, 100%, 40%);">+-           (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +</span><br><span style="color: hsl(120, 100%, 40%);">++           (count+2) * (sizeof(pj_timer_entry_dup*)+sizeof(pj_timer_id_t)+</span><br><span style="color: hsl(120, 100%, 40%);">++           sizeof(pj_timer_entry_dup)) +</span><br><span style="color: hsl(120, 100%, 40%);">+            /* lock, pool etc: */</span><br><span style="color: hsl(120, 100%, 40%);">+            132;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -391,7 +524,7 @@ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,</span><br><span style="color: hsl(120, 100%, 40%);">+     size += 2;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /* Allocate timer heap data structure from the pool */</span><br><span style="color: hsl(120, 100%, 40%);">+-    ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t);</span><br><span style="color: hsl(120, 100%, 40%);">++    ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ht)</span><br><span style="color: hsl(120, 100%, 40%);">+         return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -407,11 +540,19 @@ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->auto_delete_lock = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     // Create the heap array.</span><br><span style="color: hsl(120, 100%, 40%);">+-    ht->heap = (pj_timer_entry**)</span><br><span style="color: hsl(120, 100%, 40%);">+-                   pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);</span><br><span style="color: hsl(120, 100%, 40%);">++    ht->heap = (pj_timer_entry_dup**)</span><br><span style="color: hsl(120, 100%, 40%);">++              pj_pool_calloc(pool, size, sizeof(pj_timer_entry_dup*));</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!ht->heap)</span><br><span style="color: hsl(120, 100%, 40%);">+         return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">++    // Create the timer entry copies array.</span><br><span style="color: hsl(120, 100%, 40%);">++    ht->timer_dups = (pj_timer_entry_dup*)</span><br><span style="color: hsl(120, 100%, 40%);">++                         pj_pool_alloc(pool, sizeof(pj_timer_entry_dup) * size);</span><br><span style="color: hsl(120, 100%, 40%);">++    if (!ht->timer_dups)</span><br><span style="color: hsl(120, 100%, 40%);">++        return PJ_ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     // Create the parallel</span><br><span style="color: hsl(120, 100%, 40%);">+     ht->timer_ids = (pj_timer_id_t *)</span><br><span style="color: hsl(120, 100%, 40%);">+                        pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -467,7 +608,9 @@ PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,</span><br><span style="color: hsl(120, 100%, 40%);">+     entry->id = id;</span><br><span style="color: hsl(120, 100%, 40%);">+     entry->user_data = user_data;</span><br><span style="color: hsl(120, 100%, 40%);">+     entry->cb = cb;</span><br><span style="color: hsl(120, 100%, 40%);">++#if !PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">+     entry->_grp_lock = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     return entry;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -504,10 +647,6 @@ static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Prevent same entry from being scheduled more than once */</span><br><span style="color: hsl(120, 100%, 40%);">+     //PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-#if PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">+-    entry->src_file = src_file;</span><br><span style="color: hsl(120, 100%, 40%);">+-    entry->src_line = src_line;</span><br><span style="color: hsl(120, 100%, 40%);">+-#endif</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_gettickcount(&expires);</span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_TIME_VAL_ADD(expires, *delay);</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -516,19 +655,25 @@ static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Prevent same entry from being scheduled more than once */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (pj_timer_entry_running(entry)) {</span><br><span style="color: hsl(120, 100%, 40%);">+       unlock_timer_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+-       PJ_LOG(3,(THIS_FILE, "Bug! Rescheduling outstanding entry (%p)",</span><br><span style="color: hsl(120, 100%, 40%);">++   PJ_LOG(3,(THIS_FILE, "Warning! Rescheduling outstanding entry (%p)",</span><br><span style="color: hsl(120, 100%, 40%);">+                  entry));</span><br><span style="color: hsl(120, 100%, 40%);">+    return PJ_EINVALIDOP;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     status = schedule_entry(ht, entry, &expires);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (status == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++           pj_timer_entry_dup *timer_copy = GET_TIMER(ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     if (set_id)</span><br><span style="color: hsl(120, 100%, 40%);">+-      entry->id = id_val;</span><br><span style="color: hsl(120, 100%, 40%);">+-   entry->_grp_lock = grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+-      if (entry->_grp_lock) {</span><br><span style="color: hsl(120, 100%, 40%);">+-       pj_grp_lock_add_ref(entry->_grp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">++            GET_FIELD(timer_copy, id) = entry->id = id_val;</span><br><span style="color: hsl(120, 100%, 40%);">++       timer_copy->_grp_lock = grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">++ if (timer_copy->_grp_lock) {</span><br><span style="color: hsl(120, 100%, 40%);">++          pj_grp_lock_add_ref(timer_copy->_grp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">++       timer_copy->src_file = src_file;</span><br><span style="color: hsl(120, 100%, 40%);">++          timer_copy->src_line = src_line;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     unlock_timer_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -583,20 +728,23 @@ static int cancel_timer(pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+                      unsigned flags,</span><br><span style="color: hsl(120, 100%, 40%);">+                       int id_val)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_dup *timer_copy;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_grp_lock_t *grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+     int count;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     lock_timer_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">++    timer_copy = GET_TIMER(ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">++    grp_lock = timer_copy->_grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     count = cancel(ht, entry, flags | F_DONT_CALL);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (count > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+         /* Timer entry found & cancelled */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (flags & F_SET_ID) {</span><br><span style="color: hsl(120, 100%, 40%);">+       entry->id = id_val;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+-    if (entry->_grp_lock) {</span><br><span style="color: hsl(120, 100%, 40%);">+-       pj_grp_lock_t *grp_lock = entry->_grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+-       entry->_grp_lock = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">++      if (grp_lock) {</span><br><span style="color: hsl(120, 100%, 40%);">+           pj_grp_lock_dec_ref(grp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -640,25 +788,44 @@ PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+        PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&</span><br><span style="color: hsl(120, 100%, 40%);">+             count < ht->max_entries_per_poll ) </span><br><span style="color: hsl(120, 100%, 40%);">+     {</span><br><span style="color: hsl(120, 100%, 40%);">+- pj_timer_entry *node = remove_node(ht, 0);</span><br><span style="color: hsl(120, 100%, 40%);">++   pj_timer_entry_dup *node = remove_node(ht, 0);</span><br><span style="color: hsl(120, 100%, 40%);">++       pj_timer_entry *entry = GET_ENTRY(node);</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Avoid re-use of this timer until the callback is done. */</span><br><span style="color: hsl(120, 100%, 40%);">+  ///Not necessary, even causes problem (see also #2176).</span><br><span style="color: hsl(120, 100%, 40%);">+       ///pj_timer_id_t node_timer_id = pop_freelist(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+    pj_grp_lock_t *grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">++     pj_bool_t valid = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+         ++count;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+   grp_lock = node->_grp_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+        node->_grp_lock = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">++   if (GET_FIELD(node, cb) != entry->cb ||</span><br><span style="color: hsl(120, 100%, 40%);">++       GET_FIELD(node, user_data) != entry->user_data)</span><br><span style="color: hsl(120, 100%, 40%);">++       {</span><br><span style="color: hsl(120, 100%, 40%);">++        valid = PJ_FALSE;</span><br><span style="color: hsl(120, 100%, 40%);">++            PJ_LOG(3,(THIS_FILE, "Bug! Polling entry %p from %s line %d has "</span><br><span style="color: hsl(120, 100%, 40%);">++                               "been deallocated without being cancelled",</span><br><span style="color: hsl(120, 100%, 40%);">++                                GET_ENTRY(node),</span><br><span style="color: hsl(120, 100%, 40%);">++#if PJ_TIMER_DEBUG</span><br><span style="color: hsl(120, 100%, 40%);">++                                node->src_file, node->src_line));</span><br><span style="color: hsl(120, 100%, 40%);">++#else</span><br><span style="color: hsl(120, 100%, 40%);">++                              "N/A", 0));</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++#if ASSERT_IF_ENTRY_DESTROYED</span><br><span style="color: hsl(120, 100%, 40%);">++          pj_assert(node->dup.cb == entry->cb);</span><br><span style="color: hsl(120, 100%, 40%);">++          pj_assert(node->dup.user_data == entry->user_data);</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++       }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+  unlock_timer_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_RACE_ME(5);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-    if (node->cb)</span><br><span style="color: hsl(120, 100%, 40%);">+-         (*node->cb)(ht, node);</span><br><span style="color: hsl(120, 100%, 40%);">++        if (valid && entry->cb)</span><br><span style="color: hsl(120, 100%, 40%);">++       (*entry->cb)(ht, entry);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-   if (grp_lock)</span><br><span style="color: hsl(120, 100%, 40%);">++        if (valid && grp_lock)</span><br><span style="color: hsl(120, 100%, 40%);">+            pj_grp_lock_dec_ref(grp_lock);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+         lock_timer_heap(ht);</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -719,7 +886,7 @@ PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">+        pj_gettickcount(&now);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+         for (i=0; i<(unsigned)ht->cur_size; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+-         pj_timer_entry *e = ht->heap[i];</span><br><span style="color: hsl(120, 100%, 40%);">++          pj_timer_entry_dup *e = ht->heap[i];</span><br><span style="color: hsl(120, 100%, 40%);">+       pj_time_val delta;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+         if (PJ_TIME_VAL_LTE(e->_timer_value, now))</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -730,7 +897,7 @@ PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht)</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+          PJ_LOG(3,(THIS_FILE, "    %d\t%d\t%d.%03d\t%s:%d",</span><br><span style="color: hsl(120, 100%, 40%);">+-                   e->_timer_id, e->id,</span><br><span style="color: hsl(120, 100%, 40%);">++                   GET_FIELD(e, _timer_id), GET_FIELD(e, id),</span><br><span style="color: hsl(120, 100%, 40%);">+                    (int)delta.sec, (int)delta.msec,</span><br><span style="color: hsl(120, 100%, 40%);">+                      e->src_file, e->src_line));</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/src/pjlib-test/timer.c b/pjlib/src/pjlib-test/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+index e43160bd5..83c38704b 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/src/pjlib-test/timer.c</span><br><span>++++ b/pjlib/src/pjlib-test/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -188,19 +188,40 @@ static int test_timer_heap(void)</span><br><span style="color: hsl(120, 100%, 40%);">+ /***************</span><br><span style="color: hsl(120, 100%, 40%);">+  * Stress test *</span><br><span style="color: hsl(120, 100%, 40%);">+  ***************</span><br><span style="color: hsl(120, 100%, 40%);">+- * Test scenario:</span><br><span style="color: hsl(120, 100%, 40%);">++ * Test scenario (if RANDOMIZED_TEST is 0):</span><br><span style="color: hsl(120, 100%, 40%);">+  * 1. Create and schedule a number of timer entries.</span><br><span style="color: hsl(120, 100%, 40%);">+  * 2. Start threads for polling (simulating normal worker thread).</span><br><span style="color: hsl(120, 100%, 40%);">+  *    Each expired entry will try to cancel and re-schedule itself</span><br><span style="color: hsl(120, 100%, 40%);">+  *    from within the callback.</span><br><span style="color: hsl(120, 100%, 40%);">+  * 3. Start threads for cancelling random entries. Each successfully</span><br><span style="color: hsl(120, 100%, 40%);">+  *    cancelled entry will be re-scheduled after some random delay.</span><br><span style="color: hsl(120, 100%, 40%);">++ *</span><br><span style="color: hsl(120, 100%, 40%);">++ * Test scenario (if RANDOMIZED_TEST is 1):</span><br><span style="color: hsl(120, 100%, 40%);">++ * 1. Create and schedule a number of timer entries.</span><br><span style="color: hsl(120, 100%, 40%);">++ * 2. Start threads which will, based on a configurable probability</span><br><span style="color: hsl(120, 100%, 40%);">++ *    setting, randomly perform timer scheduling, cancelling, or</span><br><span style="color: hsl(120, 100%, 40%);">++ *    polling (simulating normal worker thread).</span><br><span style="color: hsl(120, 100%, 40%);">++ * This test is considered a failure if:</span><br><span style="color: hsl(120, 100%, 40%);">++ * - It triggers assertion/crash.</span><br><span style="color: hsl(120, 100%, 40%);">++ * - There's an error message in the log, which indicates a potential</span><br><span style="color: hsl(120, 100%, 40%);">++ *   bug in the implementation (note that race message is ok).</span><br><span style="color: hsl(120, 100%, 40%);">+  */</span><br><span style="color: hsl(120, 100%, 40%);">+-#define ST_POLL_THREAD_COUNT          10</span><br><span style="color: hsl(120, 100%, 40%);">+-#define ST_CANCEL_THREAD_COUNT     10</span><br><span style="color: hsl(120, 100%, 40%);">++#define RANDOMIZED_TEST 1</span><br><span style="color: hsl(120, 100%, 40%);">++#define SIMULATE_CRASH     PJ_TIMER_HEAP_USE_COPY</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-#define ST_ENTRY_COUNT                  1000</span><br><span style="color: hsl(120, 100%, 40%);">+-#define ST_ENTRY_MAX_TIMEOUT_MS          100</span><br><span style="color: hsl(120, 100%, 40%);">++#if RANDOMIZED_TEST</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_STRESS_THREAD_COUNT          20</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_POLL_THREAD_COUNT           0</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_CANCEL_THREAD_COUNT          0</span><br><span style="color: hsl(120, 100%, 40%);">++#else</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_STRESS_THREAD_COUNT          0</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_POLL_THREAD_COUNT            10</span><br><span style="color: hsl(120, 100%, 40%);">++    #define ST_CANCEL_THREAD_COUNT         10</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#define ST_ENTRY_COUNT                 10000</span><br><span style="color: hsl(120, 100%, 40%);">++#define ST_DURATION             30000</span><br><span style="color: hsl(120, 100%, 40%);">++#define ST_ENTRY_MAX_TIMEOUT_MS         ST_DURATION/10</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Number of group lock, may be zero, shared by timer entries, group lock</span><br><span style="color: hsl(120, 100%, 40%);">+  * can be useful to evaluate poll vs cancel race condition scenario, i.e:</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -215,6 +236,10 @@ struct thread_param</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_heap_t *timer;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_bool_t stopping;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_entry *entries;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_t **status;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_t *n_sched, *n_cancel, *n_poll;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_grp_lock_t **grp_locks;</span><br><span style="color: hsl(120, 100%, 40%);">++    int err;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     pj_atomic_t *idx;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct {</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -226,8 +251,14 @@ struct thread_param</span><br><span style="color: hsl(120, 100%, 40%);">+ static pj_status_t st_schedule_entry(pj_timer_heap_t *ht, pj_timer_entry *e)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_time_val delay = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_grp_lock_t *grp_lock = (pj_grp_lock_t*)e->user_data;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_grp_lock_t *grp_lock = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">++    struct thread_param *tparam = (struct thread_param *)e->user_data;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    if (ST_ENTRY_GROUP_LOCK_COUNT && pj_rand() % 10) {</span><br><span style="color: hsl(120, 100%, 40%);">++    /* About 90% of entries should have group lock */</span><br><span style="color: hsl(120, 100%, 40%);">++    grp_lock = tparam->grp_locks[pj_rand() % ST_ENTRY_GROUP_LOCK_COUNT];</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     delay.msec = pj_rand() % ST_ENTRY_MAX_TIMEOUT_MS;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_time_val_normalize(&delay);</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -235,8 +266,22 @@ static pj_status_t st_schedule_entry(pj_timer_heap_t *ht, pj_timer_entry *e)</span><br><span style="color: hsl(120, 100%, 40%);">+     return status;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++static void dummy_callback(pj_timer_heap_t *ht, pj_timer_entry *e)</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(4,("test", "dummy callback called %p %p", e, e->user_data));</span><br><span style="color: hsl(120, 100%, 40%);">++}</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+ static void st_entry_callback(pj_timer_heap_t *ht, pj_timer_entry *e)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">++    struct thread_param *tparam = (struct thread_param *)e->user_data;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#if RANDOMIZED_TEST</span><br><span style="color: hsl(120, 100%, 40%);">++    /* Make sure the flag has been set. */</span><br><span style="color: hsl(120, 100%, 40%);">++    while (pj_atomic_get(tparam->status[e - tparam->entries]) != 1)</span><br><span style="color: hsl(120, 100%, 40%);">++        pj_thread_sleep(10);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_set(tparam->status[e - tparam->entries], 0);</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     /* try to cancel this */</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_heap_cancel_if_active(ht, e, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -244,7 +289,118 @@ static void st_entry_callback(pj_timer_heap_t *ht, pj_timer_entry *e)</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_thread_sleep(pj_rand() % 50);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /* reschedule entry */</span><br><span style="color: hsl(120, 100%, 40%);">+-    st_schedule_entry(ht, e);</span><br><span style="color: hsl(120, 100%, 40%);">++    if (!ST_STRESS_THREAD_COUNT)</span><br><span style="color: hsl(120, 100%, 40%);">++            st_schedule_entry(ht, e);</span><br><span style="color: hsl(120, 100%, 40%);">++}</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++/* Randomized stress worker thread function. */</span><br><span style="color: hsl(120, 100%, 40%);">++static int stress_worker(void *arg)</span><br><span style="color: hsl(120, 100%, 40%);">++{</span><br><span style="color: hsl(120, 100%, 40%);">++    /* Enumeration of possible task. */</span><br><span style="color: hsl(120, 100%, 40%);">++    enum {</span><br><span style="color: hsl(120, 100%, 40%);">++          SCHEDULING = 0,</span><br><span style="color: hsl(120, 100%, 40%);">++      CANCELLING = 1,</span><br><span style="color: hsl(120, 100%, 40%);">++      POLLING = 2,</span><br><span style="color: hsl(120, 100%, 40%);">++         NOTHING = 3</span><br><span style="color: hsl(120, 100%, 40%);">++    };</span><br><span style="color: hsl(120, 100%, 40%);">++    /* Probability of a certain task being chosen.</span><br><span style="color: hsl(120, 100%, 40%);">++     * The first number indicates the probability of the first task,</span><br><span style="color: hsl(120, 100%, 40%);">++     * the second number for the second task, and so on.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    int prob[3] = {75, 15, 5};</span><br><span style="color: hsl(120, 100%, 40%);">++    struct thread_param *tparam = (struct thread_param*)arg;</span><br><span style="color: hsl(120, 100%, 40%);">++    int t_idx, i;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    t_idx = pj_atomic_inc_and_get(tparam->idx);</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(4,("test", "...thread #%d (random) started", t_idx));</span><br><span style="color: hsl(120, 100%, 40%);">++    while (!tparam->stopping) {</span><br><span style="color: hsl(120, 100%, 40%);">++        int job, task;</span><br><span style="color: hsl(120, 100%, 40%);">++       int idx, count;</span><br><span style="color: hsl(120, 100%, 40%);">++      pj_status_t prev_status, status;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++          /* Randomly choose which task to do */</span><br><span style="color: hsl(120, 100%, 40%);">++       job = pj_rand() % 100;</span><br><span style="color: hsl(120, 100%, 40%);">++       if (job < prob[0]) task = SCHEDULING;</span><br><span style="color: hsl(120, 100%, 40%);">++     else if (job < (prob[0] + prob[1])) task = CANCELLING;</span><br><span style="color: hsl(120, 100%, 40%);">++            else if (job < (prob[0] + prob[1] + prob[2])) task = POLLING;</span><br><span style="color: hsl(120, 100%, 40%);">++     else task = NOTHING;</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">++          idx = pj_rand() % ST_ENTRY_COUNT;</span><br><span style="color: hsl(120, 100%, 40%);">++            prev_status = pj_atomic_get(tparam->status[idx]);</span><br><span style="color: hsl(120, 100%, 40%);">++         if (task == SCHEDULING) {</span><br><span style="color: hsl(120, 100%, 40%);">++                if (prev_status != 0) continue;</span><br><span style="color: hsl(120, 100%, 40%);">++              status = st_schedule_entry(tparam->timer, &tparam->entries[idx]);</span><br><span style="color: hsl(120, 100%, 40%);">++          if (prev_status == 0 && status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++                  /* To make sure the flag has been set. */</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_thread_sleep(20);</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (pj_atomic_get(tparam->status[idx]) == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">++                        /* Race condition with another scheduling. */</span><br><span style="color: hsl(120, 100%, 40%);">++                        PJ_LOG(3,("test", "race schedule-schedule %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                         idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++                   } else {</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (tparam->err != 0) tparam->err = -210;</span><br><span style="color: hsl(120, 100%, 40%);">++              PJ_LOG(3,("test", "error: failed to schedule entry %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++           }</span><br><span style="color: hsl(120, 100%, 40%);">++                } else if (prev_status == 1 && status == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++                   /* Race condition with another cancellation or</span><br><span style="color: hsl(120, 100%, 40%);">++                * timer poll.</span><br><span style="color: hsl(120, 100%, 40%);">++                */</span><br><span style="color: hsl(120, 100%, 40%);">++                  pj_thread_sleep(20);</span><br><span style="color: hsl(120, 100%, 40%);">++                 PJ_LOG(3,("test", "race schedule-cancel/poll %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                      idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++           }</span><br><span style="color: hsl(120, 100%, 40%);">++            if (status == PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++              pj_atomic_set(tparam->status[idx], 1);</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_atomic_inc(tparam->n_sched);</span><br><span style="color: hsl(120, 100%, 40%);">++               }</span><br><span style="color: hsl(120, 100%, 40%);">++        } else if (task == CANCELLING) {</span><br><span style="color: hsl(120, 100%, 40%);">++         count = pj_timer_heap_cancel_if_active(tparam->timer,</span><br><span style="color: hsl(120, 100%, 40%);">++                                            &tparam->entries[idx], 10);</span><br><span style="color: hsl(120, 100%, 40%);">++            if (prev_status == 0 && count > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">++                  /* To make sure the flag has been set. */</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_thread_sleep(20);</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (pj_atomic_get(tparam->status[idx]) == 1) {</span><br><span style="color: hsl(120, 100%, 40%);">++                        /* Race condition with scheduling. */</span><br><span style="color: hsl(120, 100%, 40%);">++                        PJ_LOG(3,("test", "race cancel-schedule %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                   idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++                   } else {</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (tparam->err != 0) tparam->err = -220;</span><br><span style="color: hsl(120, 100%, 40%);">++              PJ_LOG(3,("test", "error: cancelling invalid entry %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++           }</span><br><span style="color: hsl(120, 100%, 40%);">++                } else if (prev_status == 1 && count == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">++             /* To make sure the flag has been cleared. */</span><br><span style="color: hsl(120, 100%, 40%);">++                pj_thread_sleep(20);</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (pj_atomic_get(tparam->status[idx]) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">++                        /* Race condition with polling. */</span><br><span style="color: hsl(120, 100%, 40%);">++                   PJ_LOG(3,("test", "race cancel-poll %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                       idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++                   } else {</span><br><span style="color: hsl(120, 100%, 40%);">++                 if (tparam->err != 0) tparam->err = -230;</span><br><span style="color: hsl(120, 100%, 40%);">++                      PJ_LOG(3,("test", "error: failed to cancel entry %d: %p",</span><br><span style="color: hsl(120, 100%, 40%);">++                                          idx, &tparam->entries[idx]));</span><br><span style="color: hsl(120, 100%, 40%);">++                   }</span><br><span style="color: hsl(120, 100%, 40%);">++                }</span><br><span style="color: hsl(120, 100%, 40%);">++            if (count > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">++              /* Make sure the flag has been set. */</span><br><span style="color: hsl(120, 100%, 40%);">++               while (pj_atomic_get(tparam->status[idx]) != 1)</span><br><span style="color: hsl(120, 100%, 40%);">++                       pj_thread_sleep(10);</span><br><span style="color: hsl(120, 100%, 40%);">++             pj_atomic_set(tparam->status[idx], 0);</span><br><span style="color: hsl(120, 100%, 40%);">++                    pj_atomic_inc(tparam->n_cancel);</span><br><span style="color: hsl(120, 100%, 40%);">++              }</span><br><span style="color: hsl(120, 100%, 40%);">++        } else if (task == POLLING) {</span><br><span style="color: hsl(120, 100%, 40%);">++            count = pj_timer_heap_poll(tparam->timer, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">++          for (i = 0; i < count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">++         pj_atomic_inc_and_get(tparam->n_poll);</span><br><span style="color: hsl(120, 100%, 40%);">++        }</span><br><span style="color: hsl(120, 100%, 40%);">++        } else {</span><br><span style="color: hsl(120, 100%, 40%);">++         pj_thread_sleep(10);</span><br><span style="color: hsl(120, 100%, 40%);">++     }</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(4,("test", "...thread #%d (poll) stopped", t_idx));</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Poll worker thread function. */</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -307,18 +463,26 @@ static int cancel_worker(void *arg)</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ static int timer_stress_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+ {</span><br><span style="color: hsl(120, 100%, 40%);">++    unsigned count = 0, n_sched = 0, n_cancel = 0, n_poll = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     int i;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_entry *entries = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_t      **entries_status = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_grp_lock_t **grp_locks = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_pool_t *pool;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_heap_t *timer = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_lock_t *timer_lock;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_status_t status;</span><br><span style="color: hsl(120, 100%, 40%);">+     int err=0;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_thread_t **stress_threads = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_thread_t **poll_threads = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_thread_t **cancel_threads = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct thread_param tparam = {0};</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_time_val now;</span><br><span style="color: hsl(120, 100%, 40%);">++#if SIMULATE_CRASH</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry *entry;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_pool_t *tmp_pool;</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_time_val delay = {0};</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_LOG(3,("test", "...Stress test"));</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+@@ -332,8 +496,11 @@ static int timer_stress_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+        goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-    /* Create timer heap */</span><br><span style="color: hsl(120, 100%, 40%);">+-    status = pj_timer_heap_create(pool, ST_ENTRY_COUNT, &timer);</span><br><span style="color: hsl(120, 100%, 40%);">++    /* Create timer heap.</span><br><span style="color: hsl(120, 100%, 40%);">++     * Initially we only create a fraction of what's required,</span><br><span style="color: hsl(120, 100%, 40%);">++     * to test the timer heap growth algorithm.</span><br><span style="color: hsl(120, 100%, 40%);">++     */</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_timer_heap_create(pool, ST_ENTRY_COUNT/64, &timer);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+         app_perror("...error: unable to create timer heap", status);</span><br><span style="color: hsl(120, 100%, 40%);">+     err = -20;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -354,6 +521,7 @@ static int timer_stress_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+    grp_locks = (pj_grp_lock_t**)</span><br><span style="color: hsl(120, 100%, 40%);">+                     pj_pool_calloc(pool, ST_ENTRY_GROUP_LOCK_COUNT,</span><br><span style="color: hsl(120, 100%, 40%);">+                              sizeof(pj_grp_lock_t*));</span><br><span style="color: hsl(120, 100%, 40%);">++          tparam.grp_locks = grp_locks;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i=0; i<ST_ENTRY_GROUP_LOCK_COUNT; ++i) {    </span><br><span style="color: hsl(120, 100%, 40%);">+      status = pj_grp_lock_create(pool, NULL, &grp_locks[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -372,33 +540,69 @@ static int timer_stress_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+         err = -50;</span><br><span style="color: hsl(120, 100%, 40%);">+    goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+-</span><br><span style="color: hsl(120, 100%, 40%);">++    entries_status = (pj_atomic_t**)pj_pool_calloc(pool, ST_ENTRY_COUNT,</span><br><span style="color: hsl(120, 100%, 40%);">++                                                   sizeof(*entries_status));</span><br><span style="color: hsl(120, 100%, 40%);">++    if (!entries_status) {</span><br><span style="color: hsl(120, 100%, 40%);">++    err = -55;</span><br><span style="color: hsl(120, 100%, 40%);">++   goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    </span><br><span style="color: hsl(120, 100%, 40%);">+     for (i=0; i<ST_ENTRY_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+-   pj_grp_lock_t *grp_lock = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+-</span><br><span style="color: hsl(120, 100%, 40%);">+-   if (ST_ENTRY_GROUP_LOCK_COUNT && pj_rand() % 10) {</span><br><span style="color: hsl(120, 100%, 40%);">+-       /* About 90% of entries should have group lock */</span><br><span style="color: hsl(120, 100%, 40%);">+-            grp_lock = grp_locks[pj_rand() % ST_ENTRY_GROUP_LOCK_COUNT];</span><br><span style="color: hsl(120, 100%, 40%);">+-     }</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_init(&entries[i], 0, &tparam, &st_entry_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-     pj_timer_entry_init(&entries[i], 0, grp_lock, &st_entry_callback);</span><br><span style="color: hsl(120, 100%, 40%);">+-   status = st_schedule_entry(timer, &entries[i]);</span><br><span style="color: hsl(120, 100%, 40%);">++  status = pj_atomic_create(pool, -1, &entries_status[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+-      app_perror("...error: unable to schedule entry", status);</span><br><span style="color: hsl(120, 100%, 40%);">+           err = -60;</span><br><span style="color: hsl(120, 100%, 40%);">+            goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_set(entries_status[i], 0);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++      /* For randomized test, we schedule the entry inside the thread */</span><br><span style="color: hsl(120, 100%, 40%);">++   if (!ST_STRESS_THREAD_COUNT) {</span><br><span style="color: hsl(120, 100%, 40%);">++           status = st_schedule_entry(timer, &entries[i]);</span><br><span style="color: hsl(120, 100%, 40%);">++          if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++              app_perror("...error: unable to schedule entry", status);</span><br><span style="color: hsl(120, 100%, 40%);">++          err = -60;</span><br><span style="color: hsl(120, 100%, 40%);">++           goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">++          }</span><br><span style="color: hsl(120, 100%, 40%);">++        }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     tparam.stopping = PJ_FALSE;</span><br><span style="color: hsl(120, 100%, 40%);">+     tparam.timer = timer;</span><br><span style="color: hsl(120, 100%, 40%);">+     tparam.entries = entries;</span><br><span style="color: hsl(120, 100%, 40%);">++    tparam.status = entries_status;</span><br><span style="color: hsl(120, 100%, 40%);">+     status = pj_atomic_create(pool, -1, &tparam.idx);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+        app_perror("...error: unable to create atomic", status);</span><br><span style="color: hsl(120, 100%, 40%);">+    err = -70;</span><br><span style="color: hsl(120, 100%, 40%);">+    goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_atomic_create(pool, -1, &tparam.n_sched);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert (status == PJ_SUCCESS);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_set(tparam.n_sched, 0);</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_atomic_create(pool, -1, &tparam.n_cancel);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert (status == PJ_SUCCESS);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_set(tparam.n_cancel, 0);</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_atomic_create(pool, -1, &tparam.n_poll);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert (status == PJ_SUCCESS);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_set(tparam.n_poll, 0);</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++    /* Start stress worker threads */</span><br><span style="color: hsl(120, 100%, 40%);">++    if (ST_STRESS_THREAD_COUNT) {</span><br><span style="color: hsl(120, 100%, 40%);">++       stress_threads = (pj_thread_t**)</span><br><span style="color: hsl(120, 100%, 40%);">++                     pj_pool_calloc(pool, ST_STRESS_THREAD_COUNT,</span><br><span style="color: hsl(120, 100%, 40%);">++                                sizeof(pj_thread_t*));</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    for (i=0; i<ST_STRESS_THREAD_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_thread_create( pool, "poll", &stress_worker, &tparam,</span><br><span style="color: hsl(120, 100%, 40%);">++                             0, 0, &stress_threads[i]);</span><br><span style="color: hsl(120, 100%, 40%);">++    if (status != PJ_SUCCESS) {</span><br><span style="color: hsl(120, 100%, 40%);">++      app_perror("...error: unable to create stress thread", status);</span><br><span style="color: hsl(120, 100%, 40%);">++            err = -75;</span><br><span style="color: hsl(120, 100%, 40%);">++           goto on_return;</span><br><span style="color: hsl(120, 100%, 40%);">++  }</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /* Start poll worker threads */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (ST_POLL_THREAD_COUNT) {</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -432,15 +636,38 @@ static int timer_stress_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-    /* Wait 30s */</span><br><span style="color: hsl(120, 100%, 40%);">+-    pj_thread_sleep(30*1000);</span><br><span style="color: hsl(120, 100%, 40%);">++#if SIMULATE_CRASH</span><br><span style="color: hsl(120, 100%, 40%);">++    tmp_pool = pj_pool_create( mem, NULL, 4096, 128, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert(tmp_pool);</span><br><span style="color: hsl(120, 100%, 40%);">++    entry = (pj_timer_entry*)pj_pool_calloc(tmp_pool, 1, sizeof(*entry));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert(entry);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_timer_entry_init(entry, 0, &tparam, &dummy_callback);</span><br><span style="color: hsl(120, 100%, 40%);">++    delay.sec = 6;</span><br><span style="color: hsl(120, 100%, 40%);">++    status = pj_timer_heap_schedule(timer, entry, &delay);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_assert(status == PJ_SUCCESS);</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_thread_sleep(1000);</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(3,("test", "...Releasing timer entry %p without cancelling it",</span><br><span style="color: hsl(120, 100%, 40%);">++                      entry));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_pool_secure_release(tmp_pool);</span><br><span style="color: hsl(120, 100%, 40%);">++    //pj_pool_release(tmp_pool);</span><br><span style="color: hsl(120, 100%, 40%);">++    //pj_memset(tmp_pool, 128, 4096);</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++    /* Wait */</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_thread_sleep(ST_DURATION);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ on_return:</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_LOG(3,("test", "...Cleaning up resources"));</span><br><span style="color: hsl(120, 100%, 40%);">+     tparam.stopping = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">+     </span><br><span style="color: hsl(120, 100%, 40%);">++    for (i=0; i<ST_STRESS_THREAD_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">++      if (!stress_threads[i])</span><br><span style="color: hsl(120, 100%, 40%);">++          continue;</span><br><span style="color: hsl(120, 100%, 40%);">++        pj_thread_join(stress_threads[i]);</span><br><span style="color: hsl(120, 100%, 40%);">++   pj_thread_destroy(stress_threads[i]);</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     for (i=0; i<ST_POLL_THREAD_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!poll_threads[i])</span><br><span style="color: hsl(120, 100%, 40%);">+             continue;</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -462,7 +689,9 @@ on_return:</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     for (i=0; i<ST_ENTRY_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+- pj_timer_heap_cancel_if_active(timer, &entries[i], 10);</span><br><span style="color: hsl(120, 100%, 40%);">++  count += pj_timer_heap_cancel_if_active(timer, &entries[i], 10);</span><br><span style="color: hsl(120, 100%, 40%);">++ if (entries_status)</span><br><span style="color: hsl(120, 100%, 40%);">++      pj_atomic_destroy(entries_status[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     for (i=0; i<ST_ENTRY_GROUP_LOCK_COUNT; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -477,12 +706,44 @@ on_return:</span><br><span style="color: hsl(120, 100%, 40%);">+     if (timer)</span><br><span style="color: hsl(120, 100%, 40%);">+        pj_timer_heap_destroy(timer);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(3,("test", "Total memory of timer heap: %d",</span><br><span style="color: hsl(120, 100%, 40%);">++                     pj_timer_heap_mem_size(ST_ENTRY_COUNT)));</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">+     if (tparam.idx)</span><br><span style="color: hsl(120, 100%, 40%);">+      pj_atomic_destroy(tparam.idx);</span><br><span style="color: hsl(120, 100%, 40%);">++    if (tparam.n_sched) {</span><br><span style="color: hsl(120, 100%, 40%);">++        n_sched = pj_atomic_get(tparam.n_sched);</span><br><span style="color: hsl(120, 100%, 40%);">++        PJ_LOG(3,("test", "Total number of scheduled entries: %d", n_sched));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_destroy(tparam.n_sched);</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    if (tparam.n_cancel) {</span><br><span style="color: hsl(120, 100%, 40%);">++        n_cancel = pj_atomic_get(tparam.n_cancel);</span><br><span style="color: hsl(120, 100%, 40%);">++        PJ_LOG(3,("test", "Total number of cancelled entries: %d", n_cancel));</span><br><span style="color: hsl(120, 100%, 40%);">++    pj_atomic_destroy(tparam.n_cancel);</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    if (tparam.n_poll) {</span><br><span style="color: hsl(120, 100%, 40%);">++        n_poll = pj_atomic_get(tparam.n_poll);</span><br><span style="color: hsl(120, 100%, 40%);">++        PJ_LOG(3,("test", "Total number of polled entries: %d", n_poll));</span><br><span style="color: hsl(120, 100%, 40%);">++      pj_atomic_destroy(tparam.n_poll);</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">++    PJ_LOG(3,("test", "Number of remaining active entries: %d", count));</span><br><span style="color: hsl(120, 100%, 40%);">++    if (n_sched) {</span><br><span style="color: hsl(120, 100%, 40%);">++        pj_bool_t match = PJ_TRUE;</span><br><span style="color: hsl(120, 100%, 40%);">++</span><br><span style="color: hsl(120, 100%, 40%);">++#if SIMULATE_CRASH</span><br><span style="color: hsl(120, 100%, 40%);">++   n_sched++;</span><br><span style="color: hsl(120, 100%, 40%);">++#endif</span><br><span style="color: hsl(120, 100%, 40%);">++        if (n_sched != (n_cancel + n_poll + count)) {</span><br><span style="color: hsl(120, 100%, 40%);">++            if (tparam.err != 0) tparam.err = -250;</span><br><span style="color: hsl(120, 100%, 40%);">++            match = PJ_FALSE;</span><br><span style="color: hsl(120, 100%, 40%);">++        }</span><br><span style="color: hsl(120, 100%, 40%);">++        PJ_LOG(3,("test", "Scheduled = cancelled + polled + remaining?: %s",</span><br><span style="color: hsl(120, 100%, 40%);">++                       (match? "yes": "no")));</span><br><span style="color: hsl(120, 100%, 40%);">++    }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     pj_pool_safe_release(&pool);</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+-    return err;</span><br><span style="color: hsl(120, 100%, 40%);">++    return (err? err: tparam.err);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+ int timer_test()</span><br><span>diff --git a/third-party/pjproject/patches/0041-pjlib_cancel_timer_0.patch b/third-party/pjproject/patches/0041-pjlib_cancel_timer_0.patch</span><br><span>new file mode 100644</span><br><span>index 0000000..09f72d8</span><br><span>--- /dev/null</span><br><span>+++ b/third-party/pjproject/patches/0041-pjlib_cancel_timer_0.patch</span><br><span>@@ -0,0 +1,39 @@</span><br><span style="color: hsl(120, 100%, 40%);">+commit 40dd48d10911f4ff9b8dfbf16428fbc9acc434ba</span><br><span style="color: hsl(120, 100%, 40%);">+Author: Riza Sulistyo <trengginas@users.noreply.github.com></span><br><span style="color: hsl(120, 100%, 40%);">+Date:   Thu Jul 9 17:47:24 2020 +0700</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    Modify timer_id check on cancel() (#2463)</span><br><span style="color: hsl(120, 100%, 40%);">+    </span><br><span style="color: hsl(120, 100%, 40%);">+    * modify timer_id check on cancel().</span><br><span style="color: hsl(120, 100%, 40%);">+    </span><br><span style="color: hsl(120, 100%, 40%);">+    * modification based on comments.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/include/pj/timer.h b/pjlib/include/pj/timer.h</span><br><span style="color: hsl(120, 100%, 40%);">+index b738a6e76..4b76ab65d 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/include/pj/timer.h</span><br><span>++++ b/pjlib/include/pj/timer.h</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -120,7 +120,10 @@ typedef struct pj_timer_entry</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     /** </span><br><span style="color: hsl(120, 100%, 40%);">+      * Internal unique timer ID, which is assigned by the timer heap. </span><br><span style="color: hsl(120, 100%, 40%);">+-     * Application should not touch this ID.</span><br><span style="color: hsl(120, 100%, 40%);">++     * Positive values indicate that the timer entry is running, </span><br><span style="color: hsl(120, 100%, 40%);">++     * while -1 means that it's not. Any other value may indicate that it </span><br><span style="color: hsl(120, 100%, 40%);">++     * hasn't been properly initialised or is in a bad state.</span><br><span style="color: hsl(120, 100%, 40%);">++     * Application should not touch this ID. </span><br><span style="color: hsl(120, 100%, 40%);">+      */</span><br><span style="color: hsl(120, 100%, 40%);">+     pj_timer_id_t _timer_id;</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+diff --git a/pjlib/src/pj/timer.c b/pjlib/src/pj/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+index 66516fce8..34966c481 100644</span><br><span style="color: hsl(120, 100%, 40%);">+--- a/pjlib/src/pj/timer.c</span><br><span>++++ b/pjlib/src/pj/timer.c</span><br><span style="color: hsl(120, 100%, 40%);">+@@ -535,7 +535,7 @@ static int cancel( pj_timer_heap_t *ht,</span><br><span style="color: hsl(120, 100%, 40%);">+     PJ_CHECK_STACK();</span><br><span style="color: hsl(120, 100%, 40%);">+ </span><br><span style="color: hsl(120, 100%, 40%);">+     // Check to see if the timer_id is out of range</span><br><span style="color: hsl(120, 100%, 40%);">+-    if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">++    if (entry->_timer_id < 1 || (pj_size_t)entry->_timer_id >= ht->max_size) {</span><br><span style="color: hsl(120, 100%, 40%);">+        entry->_timer_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+             return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14741">change 14741</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/14741"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: certified/16.8 </div>
<div style="display:none"> Gerrit-Change-Id: I1ba318b1f153a6dd7458846396e2867282b428e7 </div>
<div style="display:none"> Gerrit-Change-Number: 14741 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>