Changeset 6058 for pjproject/trunk/pjlib/src/pj/timer.c
- Timestamp:
- Sep 3, 2019 2:10:45 AM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib/src/pj/timer.c
r5971 r6058 47 47 #define DEFAULT_MAX_TIMED_OUT_PER_POLL (64) 48 48 49 /* Enable this to raise assertion in order to catch bug of timer entry 50 * which has been deallocated without being cancelled. If disabled, 51 * the timer heap will simply remove the destroyed entry (and print log) 52 * and resume normally. 53 * This setting only works if PJ_TIMER_HEAP_USE_COPY is enabled. 54 */ 55 #define ASSERT_IF_ENTRY_DESTROYED (PJ_TIMER_HEAP_USE_COPY? 0: 0) 56 57 49 58 enum 50 59 { … … 54 63 }; 55 64 65 #if PJ_TIMER_HEAP_USE_COPY 66 67 /* Duplicate/copy of the timer entry. */ 68 typedef struct pj_timer_entry_dup 69 { 70 /** 71 * The duplicate copy. 72 */ 73 pj_timer_entry dup; 74 75 /** 76 * Pointer of the original timer entry. 77 */ 78 pj_timer_entry *entry; 79 80 /** 81 * The future time when the timer expires, which the value is updated 82 * by timer heap when the timer is scheduled. 83 */ 84 pj_time_val _timer_value; 85 86 /** 87 * Internal: the group lock used by this entry, set when 88 * pj_timer_heap_schedule_w_lock() is used. 89 */ 90 pj_grp_lock_t *_grp_lock; 91 92 #if PJ_TIMER_DEBUG 93 const char *src_file; 94 int src_line; 95 #endif 96 97 } pj_timer_entry_dup; 98 99 #define GET_TIMER(ht, node) &ht->timer_dups[node->_timer_id] 100 #define GET_ENTRY(node) node->entry 101 #define GET_FIELD(node, _timer_id) node->dup._timer_id 102 103 #else 104 105 typedef pj_timer_entry pj_timer_entry_dup; 106 107 #define GET_TIMER(ht, node) node 108 #define GET_ENTRY(node) node 109 #define GET_FIELD(node, _timer_id) node->_timer_id 110 111 #endif 56 112 57 113 /** … … 84 140 * array. 85 141 */ 86 pj_timer_entry **heap;142 pj_timer_entry_dup **heap; 87 143 88 144 /** … … 97 153 */ 98 154 pj_timer_id_t *timer_ids; 155 156 /** 157 * An array of timer entry copies. 158 */ 159 pj_timer_entry_dup *timer_dups; 99 160 100 161 /** … … 127 188 128 189 static void copy_node( pj_timer_heap_t *ht, pj_size_t slot, 129 pj_timer_entry *moved_node )190 pj_timer_entry_dup *moved_node ) 130 191 { 131 192 PJ_CHECK_STACK(); … … 135 196 136 197 // Update the corresponding slot in the parallel <timer_ids_> array. 137 ht->timer_ids[ moved_node->_timer_id] = (int)slot;198 ht->timer_ids[GET_FIELD(moved_node, _timer_id)] = (int)slot; 138 199 } 139 200 … … 165 226 166 227 167 static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,228 static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node, 168 229 size_t slot, size_t child) 169 230 { … … 175 236 { 176 237 // Choose the smaller of the two children. 177 if (child + 1 < ht->cur_size 178 && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value)) 238 if (child + 1 < ht->cur_size && 239 PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, 240 ht->heap[child]->_timer_value)) 241 { 179 242 child++; 243 } 180 244 181 245 // Perform a <copy> if the child has a larger timeout value than 182 246 // the <moved_node>. 183 if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value)) 247 if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, 248 moved_node->_timer_value)) 184 249 { 185 250 copy_node( ht, slot, ht->heap[child]); … … 195 260 } 196 261 197 static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,262 static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry_dup *moved_node, 198 263 size_t slot, size_t parent) 199 264 { … … 204 269 // If the parent node is greater than the <moved_node> we need 205 270 // to copy it down. 206 if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value)) 271 if (PJ_TIME_VAL_LT(moved_node->_timer_value, 272 ht->heap[parent]->_timer_value)) 207 273 { 208 274 copy_node(ht, slot, ht->heap[parent]); … … 220 286 221 287 222 static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)223 { 224 pj_timer_entry *removed_node = ht->heap[slot];288 static pj_timer_entry_dup * remove_node( pj_timer_heap_t *ht, size_t slot) 289 { 290 pj_timer_entry_dup *removed_node = ht->heap[slot]; 225 291 226 292 // Return this timer id to the freelist. 227 push_freelist( ht, removed_node->_timer_id);293 push_freelist( ht, GET_FIELD(removed_node, _timer_id) ); 228 294 229 295 // Decrement the size of the heap by one since we're removing the … … 232 298 233 299 // Set the ID 234 removed_node->_timer_id = -1; 300 if (GET_FIELD(removed_node, _timer_id) != 301 GET_ENTRY(removed_node)->_timer_id) 302 { 303 PJ_LOG(3,(THIS_FILE, "Bug! Trying to remove entry %p from %s " 304 "line %d, which has been deallocated " 305 "without being cancelled", 306 GET_ENTRY(removed_node), 307 #if PJ_TIMER_DEBUG 308 removed_node->src_file, 309 removed_node->src_line)); 310 #else 311 "N/A", 0)); 312 #endif 313 #if ASSERT_IF_ENTRY_DESTROYED 314 pj_assert(removed_node->dup._timer_id==removed_node->entry->_timer_id); 315 #endif 316 } 317 GET_ENTRY(removed_node)->_timer_id = -1; 318 GET_FIELD(removed_node, _timer_id) = -1; 235 319 236 320 // Only try to reheapify if we're not deleting the last entry. … … 239 323 { 240 324 pj_size_t parent; 241 pj_timer_entry *moved_node = ht->heap[ht->cur_size];325 pj_timer_entry_dup *moved_node = ht->heap[ht->cur_size]; 242 326 243 327 // Move the end node to the location being removed and update … … 249 333 parent = HEAP_PARENT (slot); 250 334 251 if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value)) 335 if (PJ_TIME_VAL_GTE(moved_node->_timer_value, 336 ht->heap[parent]->_timer_value)) 337 { 252 338 reheap_down( ht, moved_node, slot, HEAP_LEFT(slot)); 253 else339 } else { 254 340 reheap_up( ht, moved_node, slot, parent); 341 } 255 342 } 256 343 … … 258 345 } 259 346 260 static voidgrow_heap(pj_timer_heap_t *ht)347 static pj_status_t grow_heap(pj_timer_heap_t *ht) 261 348 { 262 349 // All the containers will double in size from max_size_ 263 350 size_t new_size = ht->max_size * 2; 351 pj_timer_entry_dup *new_timer_dups = 0; 264 352 pj_timer_id_t *new_timer_ids; 265 353 pj_size_t i; 266 354 355 PJ_LOG(6,(THIS_FILE, "Growing heap size from %d to %d", 356 ht->max_size, new_size)); 357 267 358 // First grow the heap itself. 268 359 269 pj_timer_entry **new_heap = 0; 270 271 new_heap = (pj_timer_entry**) 272 pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size); 273 memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*)); 274 //delete [] this->heap_; 360 pj_timer_entry_dup **new_heap = 0; 361 362 new_heap = (pj_timer_entry_dup**) 363 pj_pool_calloc(ht->pool, new_size, sizeof(pj_timer_entry_dup*)); 364 if (!new_heap) 365 return PJ_ENOMEM; 366 367 #if PJ_TIMER_HEAP_USE_COPY 368 // Grow the array of timer copies. 369 370 new_timer_dups = (pj_timer_entry_dup*) 371 pj_pool_alloc(ht->pool, 372 sizeof(pj_timer_entry_dup) * new_size); 373 if (!new_timer_dups) 374 return PJ_ENOMEM; 375 376 memcpy(new_timer_dups, ht->timer_dups, 377 ht->max_size * sizeof(pj_timer_entry_dup)); 378 for (i = 0; i < ht->cur_size; i++) { 379 int idx = ht->heap[i] - ht->timer_dups; 380 // Point to the address in the new array 381 pj_assert(idx >= 0 && idx < ht->max_size); 382 new_heap[i] = &new_timer_dups[idx]; 383 } 384 ht->timer_dups = new_timer_dups; 385 #else 386 memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry *)); 387 #endif 275 388 ht->heap = new_heap; 276 389 … … 280 393 new_timer_ids = (pj_timer_id_t*) 281 394 pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t)); 282 395 if (!new_timer_ids) 396 return PJ_ENOMEM; 397 283 398 memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t)); 284 399 … … 291 406 292 407 ht->max_size = new_size; 293 } 294 295 static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node) 296 { 297 if (ht->cur_size + 2 >= ht->max_size) 298 grow_heap(ht); 299 300 reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size)); 408 409 return PJ_SUCCESS; 410 } 411 412 static pj_status_t insert_node(pj_timer_heap_t *ht, 413 pj_timer_entry *new_node, 414 const pj_time_val *future_time) 415 { 416 pj_timer_entry_dup *timer_copy; 417 418 if (ht->cur_size + 2 >= ht->max_size) { 419 pj_status_t status = grow_heap(ht); 420 if (status != PJ_SUCCESS) 421 return status; 422 } 423 424 timer_copy = GET_TIMER(ht, new_node); 425 #if PJ_TIMER_HEAP_USE_COPY 426 // Create a duplicate of the timer entry. 427 pj_bzero(timer_copy, sizeof(*timer_copy)); 428 pj_memcpy(&timer_copy->dup, new_node, sizeof(*new_node)); 429 timer_copy->entry = new_node; 430 #endif 431 timer_copy->_timer_value = *future_time; 432 433 reheap_up( ht, timer_copy, ht->cur_size, HEAP_PARENT(ht->cur_size)); 301 434 ht->cur_size++; 435 436 return PJ_SUCCESS; 302 437 } 303 438 … … 312 447 // Set the entry 313 448 entry->_timer_id = pop_freelist(ht); 314 entry->_timer_value = *future_time; 315 insert_node( ht, entry); 316 return 0; 449 450 return insert_node( ht, entry, future_time ); 317 451 } 318 452 else … … 325 459 unsigned flags) 326 460 { 327 long timer_node_slot; 328 329 PJ_CHECK_STACK(); 330 331 // Check to see if the timer_id is out of range 332 if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) { 333 entry->_timer_id = -1; 334 return 0; 335 } 336 337 timer_node_slot = ht->timer_ids[entry->_timer_id]; 338 339 if (timer_node_slot < 0) { // Check to see if timer_id is still valid. 340 entry->_timer_id = -1; 341 return 0; 342 } 343 344 if (entry != ht->heap[timer_node_slot]) 345 { 346 if ((flags & F_DONT_ASSERT) == 0) 347 pj_assert(entry == ht->heap[timer_node_slot]); 348 entry->_timer_id = -1; 349 return 0; 350 } 351 else 352 { 353 remove_node( ht, timer_node_slot); 354 355 if ((flags & F_DONT_CALL) == 0) 356 // Call the close hook. 357 (*ht->callback)(ht, entry); 358 return 1; 461 long timer_node_slot; 462 463 PJ_CHECK_STACK(); 464 465 // Check to see if the timer_id is out of range 466 if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size) { 467 entry->_timer_id = -1; 468 return 0; 469 } 470 471 timer_node_slot = ht->timer_ids[entry->_timer_id]; 472 473 if (timer_node_slot < 0) { // Check to see if timer_id is still valid. 474 entry->_timer_id = -1; 475 return 0; 476 } 477 478 if (entry != GET_ENTRY(ht->heap[timer_node_slot])) { 479 if ((flags & F_DONT_ASSERT) == 0) 480 pj_assert(entry == GET_ENTRY(ht->heap[timer_node_slot])); 481 entry->_timer_id = -1; 482 return 0; 483 } else { 484 remove_node( ht, timer_node_slot); 485 486 if ((flags & F_DONT_CALL) == 0) { 487 // Call the close hook. 488 (*ht->callback)(ht, entry); 489 } 490 return 1; 359 491 } 360 492 } … … 369 501 sizeof(pj_timer_heap_t) + 370 502 /* size of each entry: */ 371 (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + 503 (count+2) * (sizeof(pj_timer_entry_dup*)+sizeof(pj_timer_id_t)+ 504 sizeof(pj_timer_entry_dup)) + 372 505 /* lock, pool etc: */ 373 506 132; … … 392 525 393 526 /* Allocate timer heap data structure from the pool */ 394 ht = PJ_POOL_ ALLOC_T(pool, pj_timer_heap_t);527 ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t); 395 528 if (!ht) 396 529 return PJ_ENOMEM; … … 408 541 409 542 // Create the heap array. 410 ht->heap = (pj_timer_entry **)411 pj_pool_ alloc(pool, sizeof(pj_timer_entry*) * size);543 ht->heap = (pj_timer_entry_dup**) 544 pj_pool_calloc(pool, size, sizeof(pj_timer_entry_dup*)); 412 545 if (!ht->heap) 413 546 return PJ_ENOMEM; 547 548 #if PJ_TIMER_HEAP_USE_COPY 549 // Create the timer entry copies array. 550 ht->timer_dups = (pj_timer_entry_dup*) 551 pj_pool_alloc(pool, sizeof(pj_timer_entry_dup) * size); 552 if (!ht->timer_dups) 553 return PJ_ENOMEM; 554 #endif 414 555 415 556 // Create the parallel … … 468 609 entry->user_data = user_data; 469 610 entry->cb = cb; 611 #if !PJ_TIMER_HEAP_USE_COPY 470 612 entry->_grp_lock = NULL; 613 #endif 471 614 472 615 return entry; … … 505 648 //PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); 506 649 507 #if PJ_TIMER_DEBUG508 entry->src_file = src_file;509 entry->src_line = src_line;510 #endif511 650 pj_gettickcount(&expires); 512 651 PJ_TIME_VAL_ADD(expires, *delay); … … 517 656 if (pj_timer_entry_running(entry)) { 518 657 unlock_timer_heap(ht); 519 PJ_LOG(3,(THIS_FILE, " Bug! Rescheduling outstanding entry (%p)",658 PJ_LOG(3,(THIS_FILE, "Warning! Rescheduling outstanding entry (%p)", 520 659 entry)); 521 660 return PJ_EINVALIDOP; … … 524 663 status = schedule_entry(ht, entry, &expires); 525 664 if (status == PJ_SUCCESS) { 665 pj_timer_entry_dup *timer_copy = GET_TIMER(ht, entry); 666 526 667 if (set_id) 527 entry->id = id_val;528 entry->_grp_lock = grp_lock;529 if ( entry->_grp_lock) {530 pj_grp_lock_add_ref( entry->_grp_lock);668 GET_FIELD(timer_copy, id) = entry->id = id_val; 669 timer_copy->_grp_lock = grp_lock; 670 if (timer_copy->_grp_lock) { 671 pj_grp_lock_add_ref(timer_copy->_grp_lock); 531 672 } 673 #if PJ_TIMER_DEBUG 674 timer_copy->src_file = src_file; 675 timer_copy->src_line = src_line; 676 #endif 532 677 } 533 678 unlock_timer_heap(ht); … … 584 729 int id_val) 585 730 { 731 pj_timer_entry_dup *timer_copy; 732 pj_grp_lock_t *grp_lock; 586 733 int count; 587 734 … … 589 736 590 737 lock_timer_heap(ht); 738 timer_copy = GET_TIMER(ht, entry); 739 grp_lock = timer_copy->_grp_lock; 740 591 741 count = cancel(ht, entry, flags | F_DONT_CALL); 592 742 if (count > 0) { … … 595 745 entry->id = id_val; 596 746 } 597 if (entry->_grp_lock) { 598 pj_grp_lock_t *grp_lock = entry->_grp_lock; 599 entry->_grp_lock = NULL; 747 if (grp_lock) { 600 748 pj_grp_lock_dec_ref(grp_lock); 601 749 } … … 641 789 count < ht->max_entries_per_poll ) 642 790 { 643 pj_timer_entry *node = remove_node(ht, 0); 791 pj_timer_entry_dup *node = remove_node(ht, 0); 792 pj_timer_entry *entry = GET_ENTRY(node); 644 793 /* Avoid re-use of this timer until the callback is done. */ 645 794 ///Not necessary, even causes problem (see also #2176). 646 795 ///pj_timer_id_t node_timer_id = pop_freelist(ht); 647 796 pj_grp_lock_t *grp_lock; 797 pj_bool_t valid = PJ_TRUE; 648 798 649 799 ++count; … … 651 801 grp_lock = node->_grp_lock; 652 802 node->_grp_lock = NULL; 803 if (GET_FIELD(node, cb) != entry->cb || 804 GET_FIELD(node, user_data) != entry->user_data) 805 { 806 valid = PJ_FALSE; 807 PJ_LOG(3,(THIS_FILE, "Bug! Polling entry %p from %s line %d has " 808 "been deallocated without being cancelled", 809 GET_ENTRY(node), 810 #if PJ_TIMER_DEBUG 811 node->src_file, node->src_line)); 812 #else 813 "N/A", 0)); 814 #endif 815 #if ASSERT_IF_ENTRY_DESTROYED 816 pj_assert(node->dup.cb == entry->cb); 817 pj_assert(node->dup.user_data == entry->user_data); 818 #endif 819 } 653 820 654 821 unlock_timer_heap(ht); … … 656 823 PJ_RACE_ME(5); 657 824 658 if ( node->cb)659 (* node->cb)(ht, node);660 661 if ( grp_lock)825 if (valid && entry->cb) 826 (*entry->cb)(ht, entry); 827 828 if (valid && grp_lock) 662 829 pj_grp_lock_dec_ref(grp_lock); 663 830 … … 720 887 721 888 for (i=0; i<(unsigned)ht->cur_size; ++i) { 722 pj_timer_entry *e = ht->heap[i];889 pj_timer_entry_dup *e = ht->heap[i]; 723 890 pj_time_val delta; 724 891 … … 731 898 732 899 PJ_LOG(3,(THIS_FILE, " %d\t%d\t%d.%03d\t%s:%d", 733 e->_timer_id, e->id,900 GET_FIELD(e, _timer_id), GET_FIELD(e, id), 734 901 (int)delta.sec, (int)delta.msec, 735 902 e->src_file, e->src_line));
Note: See TracChangeset
for help on using the changeset viewer.