Ignore:
Timestamp:
Dec 16, 2009 1:30:34 PM (9 years ago)
Author:
bennylp
Message:

Ticket #1004: Symbian timer heap fix

  • timer heap now records active timers and cancel them when it's destroyed
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/timer_symbian.cpp

    r2984 r3034  
    3232#define MAX_RTIMER_INTERVAL             2147 
    3333 
     34/* Absolute maximum number of timer entries */ 
     35#ifndef PJ_SYMBIAN_TIMER_MAX_COUNT 
     36#  define PJ_SYMBIAN_TIMER_MAX_COUNT    65535 
     37#endif 
     38 
     39/* Get the number of free slots in the timer heap */ 
     40#define FREECNT(th)     (th->max_size - th->cur_size) 
     41 
     42// Forward declaration 
     43class CPjTimerEntry; 
    3444 
    3545/** 
     
    4454    pj_size_t cur_size; 
    4555 
    46     /** Max timed out entries to process per poll. */ 
    47     unsigned max_entries_per_poll; 
     56    /** Array of timer entries. A scheduled timer will occupy one slot, and 
     57     *  the slot number will be saved in entry->_timer_id 
     58     */ 
     59    CPjTimerEntry **entries; 
     60     
     61    /** Array of free slot indexes in the "entries" array */ 
     62    int *free_slots; 
    4863}; 
    4964 
    50  
    51 ////////////////////////////////////////////////////////////////////////////// 
    5265/** 
    5366 * Active object for each timer entry. 
     
    5669{ 
    5770public: 
     71    pj_timer_entry  *entry_; 
     72     
    5873    static CPjTimerEntry* NewL( pj_timer_heap_t *timer_heap, 
    5974                                pj_timer_entry *entry, 
     
    6782private:         
    6883    pj_timer_heap_t *timer_heap_; 
    69     pj_timer_entry  *entry_; 
    7084    RTimer           rtimer_; 
    7185    pj_uint32_t      interval_left_; 
     
    7690}; 
    7791 
     92////////////////////////////////////////////////////////////////////////////// 
     93/* 
     94 * Implementation. 
     95 */ 
     96 
     97/* Grow timer heap to the specified size */ 
     98static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size) 
     99{ 
     100    typedef CPjTimerEntry *entry_ptr; 
     101    CPjTimerEntry **entries = NULL; 
     102    int *free_slots = NULL; 
     103    unsigned i, j; 
     104  
     105    if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) { 
     106        /* Just some sanity limit */ 
     107        new_size = PJ_SYMBIAN_TIMER_MAX_COUNT; 
     108        if (new_size <= th->max_size) { 
     109            /* We've grown large enough */ 
     110            pj_assert(!"Too many timer heap entries"); 
     111            return PJ_ETOOMANY; 
     112        } 
     113    } 
     114     
     115    /* Allocate entries, move entries from the old array if there is one */ 
     116    entries = new entry_ptr[new_size]; 
     117    if (th->entries) { 
     118        pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0])); 
     119    } 
     120    /* Initialize the remaining new area */ 
     121    pj_bzero(&entries[th->max_size],  
     122            (new_size - th->max_size) * sizeof(th->entries[0])); 
     123     
     124    /* Allocate free slots array */ 
     125    free_slots = new int[new_size]; 
     126    if (th->free_slots) { 
     127        pj_memcpy(free_slots, th->free_slots,  
     128                  FREECNT(th) * sizeof(th->free_slots[0])); 
     129    } 
     130    /* Initialize the remaining new area */ 
     131    for (i=FREECNT(th), j=th->max_size; j<new_size; ++i, ++j) { 
     132        free_slots[i] = j; 
     133    } 
     134    for ( ; i<new_size; ++i) { 
     135        free_slots[i] = -1; 
     136    } 
     137     
     138    /* Apply */ 
     139    delete [] th->entries; 
     140    th->entries = entries; 
     141    th->max_size = new_size; 
     142    delete [] th->free_slots; 
     143    th->free_slots = free_slots; 
     144 
     145    return PJ_SUCCESS; 
     146} 
     147 
     148/* Allocate and register an entry to timer heap for newly scheduled entry */ 
     149static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) 
     150{ 
     151    pj_status_t status; 
     152    int slot; 
     153     
     154    /* Check that there's still capacity left in the timer heap */ 
     155    if (FREECNT(th) < 1) { 
     156        // Grow the timer heap twice the capacity 
     157        status = realloc_timer_heap(th, th->max_size * 2); 
     158        if (status != PJ_SUCCESS) 
     159            return status; 
     160    } 
     161     
     162    /* Allocate one free slot. Use LIFO */ 
     163    slot = th->free_slots[FREECNT(th)-1]; 
     164    PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) &&  
     165                     (th->entries[slot]==NULL), PJ_EBUG); 
     166     
     167    th->free_slots[FREECNT(th)-1] = -1; 
     168    th->entries[slot] = entry; 
     169    entry->entry_->_timer_id = slot; 
     170    ++th->cur_size; 
     171     
     172    return PJ_SUCCESS; 
     173} 
     174 
     175/* Free a slot when an entry's timer has elapsed or cancel */ 
     176static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry) 
     177{ 
     178    int slot = entry->entry_->_timer_id; 
     179     
     180    PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG); 
     181    PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG); 
     182    PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG); 
     183    PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG); 
     184     
     185    th->entries[slot] = NULL; 
     186    th->free_slots[FREECNT(th)] = slot; 
     187    entry->entry_->_timer_id = -1; 
     188    --th->cur_size; 
     189     
     190    return PJ_SUCCESS; 
     191} 
     192 
    78193 
    79194CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap, 
    80195                             pj_timer_entry *entry) 
    81 : CActive(PJ_SYMBIAN_TIMER_PRIORITY), timer_heap_(timer_heap), entry_(entry), 
     196: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap),  
    82197  interval_left_(0) 
    83198{ 
     
    133248    } 
    134249     
    135     --timer_heap_->cur_size; 
    136     entry_->_timer_id = NULL; 
     250    remove_entry(timer_heap_, this); 
    137251    entry_->cb(timer_heap_, entry_); 
    138252     
     
    143257void CPjTimerEntry::DoCancel()  
    144258{ 
    145      rtimer_.Cancel(); 
     259    /* It's possible that _timer_id is -1, see schedule(). In this case, 
     260     * the entry has not been added to the timer heap, so don't remove 
     261     * it. 
     262     */ 
     263    if (entry_ && entry_->_timer_id != -1) 
     264        remove_entry(timer_heap_, this); 
     265     
     266    rtimer_.Cancel(); 
    146267} 
    147268 
     
    158279           sizeof(pj_timer_heap_t) +  
    159280           /* size of each entry: */ 
    160            (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) + 
     281           (count+2) * (sizeof(void*)+sizeof(int)) + 
    161282           /* lock, pool etc: */ 
    162283           132; 
     
    171292{ 
    172293    pj_timer_heap_t *ht; 
     294    pj_status_t status; 
    173295 
    174296    PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL); 
     
    177299 
    178300    /* Allocate timer heap data structure from the pool */ 
    179     ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t); 
     301    ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t); 
    180302    if (!ht) 
    181303        return PJ_ENOMEM; 
    182304 
    183     /* Initialize timer heap sizes */ 
    184     ht->max_size = size; 
    185     ht->cur_size = 0; 
    186     ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL; 
     305    /* Allocate slots */ 
     306    status = realloc_timer_heap(ht, size); 
     307    if (status != PJ_SUCCESS) 
     308        return status; 
    187309 
    188310    *p_heap = ht; 
     
    192314PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht ) 
    193315{ 
    194     PJ_UNUSED_ARG(ht); 
     316    /* Cancel and delete pending active objects */ 
     317    if (ht->entries) { 
     318        unsigned i; 
     319        for (i=0; i<ht->max_size; ++i) { 
     320            if (ht->entries[i]) { 
     321                ht->entries[i]->Cancel(); 
     322                delete ht->entries[i]; 
     323                ht->entries[i] = NULL; 
     324            } 
     325        } 
     326    } 
     327     
     328    delete [] ht->entries; 
     329    delete [] ht->free_slots; 
     330     
     331    ht->entries = NULL; 
     332    ht->free_slots = NULL; 
    195333} 
    196334 
     
    208346                                                          unsigned count ) 
    209347{ 
    210     unsigned old_count = ht->max_entries_per_poll; 
    211     ht->max_entries_per_poll = count; 
    212     return old_count; 
     348    /* Not applicable */ 
     349    PJ_UNUSED_ARG(count); 
     350    return ht->max_size; 
    213351} 
    214352 
     
    220358    pj_assert(entry && cb); 
    221359 
    222     entry->_timer_id = NULL; 
     360    entry->_timer_id = -1; 
    223361    entry->id = id; 
    224362    entry->user_data = user_data; 
     
    233371{ 
    234372    CPjTimerEntry *timerObj; 
     373    pj_status_t status; 
    235374     
    236375    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL); 
     
    238377 
    239378    /* Prevent same entry from being scheduled more than once */ 
    240     PJ_ASSERT_RETURN(entry->_timer_id == NULL, PJ_EINVALIDOP); 
    241  
     379    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP); 
     380 
     381    entry->_timer_id = -1; 
     382     
    242383    timerObj = CPjTimerEntry::NewL(ht, entry, delay); 
    243     entry->_timer_id = (void*) timerObj; 
    244      
    245     ++ht->cur_size; 
     384    status = add_entry(ht, timerObj); 
     385    if (status != PJ_SUCCESS) { 
     386        timerObj->Cancel(); 
     387        delete timerObj; 
     388        return status; 
     389    } 
     390     
    246391    return PJ_SUCCESS; 
    247392} 
     
    252397    PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL); 
    253398     
    254     if (entry->_timer_id != NULL) { 
    255         CPjTimerEntry *timerObj = (CPjTimerEntry*) entry->_timer_id; 
    256         timerObj->Cancel(); 
    257         delete timerObj; 
    258         entry->_timer_id = NULL; 
    259         --ht->cur_size; 
    260         return 1; 
     399    if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) { 
     400        CPjTimerEntry *timerObj = ht->entries[entry->_timer_id]; 
     401        if (timerObj) { 
     402            timerObj->Cancel(); 
     403            delete timerObj; 
     404            return 1; 
     405        } else { 
     406            return 0; 
     407        } 
    261408    } else { 
    262409        return 0; 
Note: See TracChangeset for help on using the changeset viewer.