Ticket #1004: Symbian timer heap fix

- timer heap now records active timers and cancel them when it's destroyed


git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@3034 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2009-12-16 13:30:34 +00:00
parent 54d642db3b
commit d53ed17152
2 changed files with 178 additions and 35 deletions

View File

@ -64,11 +64,7 @@ PJ_BEGIN_DECL
/**
* The type for internal timer ID.
*/
#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
typedef void *pj_timer_id_t;
#else
typedef int pj_timer_id_t;
#endif
/**
* Forward declaration for pj_timer_entry.

View File

@ -31,6 +31,16 @@
// Maximum number of miliseconds that RTimer.At() supports
#define MAX_RTIMER_INTERVAL 2147
/* Absolute maximum number of timer entries */
#ifndef PJ_SYMBIAN_TIMER_MAX_COUNT
# define PJ_SYMBIAN_TIMER_MAX_COUNT 65535
#endif
/* Get the number of free slots in the timer heap */
#define FREECNT(th) (th->max_size - th->cur_size)
// Forward declaration
class CPjTimerEntry;
/**
* The implementation of timer heap.
@ -43,18 +53,23 @@ struct pj_timer_heap_t
/** Current size of the heap. */
pj_size_t cur_size;
/** Max timed out entries to process per poll. */
unsigned max_entries_per_poll;
/** Array of timer entries. A scheduled timer will occupy one slot, and
* the slot number will be saved in entry->_timer_id
*/
CPjTimerEntry **entries;
/** Array of free slot indexes in the "entries" array */
int *free_slots;
};
//////////////////////////////////////////////////////////////////////////////
/**
* Active object for each timer entry.
*/
class CPjTimerEntry : public CActive
{
public:
pj_timer_entry *entry_;
static CPjTimerEntry* NewL( pj_timer_heap_t *timer_heap,
pj_timer_entry *entry,
const pj_time_val *delay);
@ -66,7 +81,6 @@ public:
private:
pj_timer_heap_t *timer_heap_;
pj_timer_entry *entry_;
RTimer rtimer_;
pj_uint32_t interval_left_;
@ -75,10 +89,111 @@ private:
void Schedule();
};
//////////////////////////////////////////////////////////////////////////////
/*
* Implementation.
*/
/* Grow timer heap to the specified size */
static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size)
{
typedef CPjTimerEntry *entry_ptr;
CPjTimerEntry **entries = NULL;
int *free_slots = NULL;
unsigned i, j;
if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) {
/* Just some sanity limit */
new_size = PJ_SYMBIAN_TIMER_MAX_COUNT;
if (new_size <= th->max_size) {
/* We've grown large enough */
pj_assert(!"Too many timer heap entries");
return PJ_ETOOMANY;
}
}
/* Allocate entries, move entries from the old array if there is one */
entries = new entry_ptr[new_size];
if (th->entries) {
pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0]));
}
/* Initialize the remaining new area */
pj_bzero(&entries[th->max_size],
(new_size - th->max_size) * sizeof(th->entries[0]));
/* Allocate free slots array */
free_slots = new int[new_size];
if (th->free_slots) {
pj_memcpy(free_slots, th->free_slots,
FREECNT(th) * sizeof(th->free_slots[0]));
}
/* Initialize the remaining new area */
for (i=FREECNT(th), j=th->max_size; j<new_size; ++i, ++j) {
free_slots[i] = j;
}
for ( ; i<new_size; ++i) {
free_slots[i] = -1;
}
/* Apply */
delete [] th->entries;
th->entries = entries;
th->max_size = new_size;
delete [] th->free_slots;
th->free_slots = free_slots;
return PJ_SUCCESS;
}
/* Allocate and register an entry to timer heap for newly scheduled entry */
static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
{
pj_status_t status;
int slot;
/* Check that there's still capacity left in the timer heap */
if (FREECNT(th) < 1) {
// Grow the timer heap twice the capacity
status = realloc_timer_heap(th, th->max_size * 2);
if (status != PJ_SUCCESS)
return status;
}
/* Allocate one free slot. Use LIFO */
slot = th->free_slots[FREECNT(th)-1];
PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) &&
(th->entries[slot]==NULL), PJ_EBUG);
th->free_slots[FREECNT(th)-1] = -1;
th->entries[slot] = entry;
entry->entry_->_timer_id = slot;
++th->cur_size;
return PJ_SUCCESS;
}
/* Free a slot when an entry's timer has elapsed or cancel */
static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
{
int slot = entry->entry_->_timer_id;
PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG);
PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG);
PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG);
PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG);
th->entries[slot] = NULL;
th->free_slots[FREECNT(th)] = slot;
entry->entry_->_timer_id = -1;
--th->cur_size;
return PJ_SUCCESS;
}
CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap,
pj_timer_entry *entry)
: CActive(PJ_SYMBIAN_TIMER_PRIORITY), timer_heap_(timer_heap), entry_(entry),
: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap),
interval_left_(0)
{
}
@ -132,8 +247,7 @@ void CPjTimerEntry::RunL()
return;
}
--timer_heap_->cur_size;
entry_->_timer_id = NULL;
remove_entry(timer_heap_, this);
entry_->cb(timer_heap_, entry_);
// Finger's crossed!
@ -142,7 +256,14 @@ void CPjTimerEntry::RunL()
void CPjTimerEntry::DoCancel()
{
rtimer_.Cancel();
/* It's possible that _timer_id is -1, see schedule(). In this case,
* the entry has not been added to the timer heap, so don't remove
* it.
*/
if (entry_ && entry_->_timer_id != -1)
remove_entry(timer_heap_, this);
rtimer_.Cancel();
}
@ -157,7 +278,7 @@ PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
return /* size of the timer heap itself: */
sizeof(pj_timer_heap_t) +
/* size of each entry: */
(count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
(count+2) * (sizeof(void*)+sizeof(int)) +
/* lock, pool etc: */
132;
}
@ -170,20 +291,21 @@ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
pj_timer_heap_t **p_heap)
{
pj_timer_heap_t *ht;
pj_status_t status;
PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
*p_heap = NULL;
/* Allocate timer heap data structure from the pool */
ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t);
ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
if (!ht)
return PJ_ENOMEM;
/* Initialize timer heap sizes */
ht->max_size = size;
ht->cur_size = 0;
ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
/* Allocate slots */
status = realloc_timer_heap(ht, size);
if (status != PJ_SUCCESS)
return status;
*p_heap = ht;
return PJ_SUCCESS;
@ -191,7 +313,23 @@ PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
{
PJ_UNUSED_ARG(ht);
/* Cancel and delete pending active objects */
if (ht->entries) {
unsigned i;
for (i=0; i<ht->max_size; ++i) {
if (ht->entries[i]) {
ht->entries[i]->Cancel();
delete ht->entries[i];
ht->entries[i] = NULL;
}
}
}
delete [] ht->entries;
delete [] ht->free_slots;
ht->entries = NULL;
ht->free_slots = NULL;
}
PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
@ -207,9 +345,9 @@ PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
unsigned count )
{
unsigned old_count = ht->max_entries_per_poll;
ht->max_entries_per_poll = count;
return old_count;
/* Not applicable */
PJ_UNUSED_ARG(count);
return ht->max_size;
}
PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
@ -219,7 +357,7 @@ PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
{
pj_assert(entry && cb);
entry->_timer_id = NULL;
entry->_timer_id = -1;
entry->id = id;
entry->user_data = user_data;
entry->cb = cb;
@ -232,17 +370,24 @@ PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
const pj_time_val *delay)
{
CPjTimerEntry *timerObj;
pj_status_t status;
PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
/* Prevent same entry from being scheduled more than once */
PJ_ASSERT_RETURN(entry->_timer_id == NULL, PJ_EINVALIDOP);
PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
timerObj = CPjTimerEntry::NewL(ht, entry, delay);
entry->_timer_id = (void*) timerObj;
entry->_timer_id = -1;
timerObj = CPjTimerEntry::NewL(ht, entry, delay);
status = add_entry(ht, timerObj);
if (status != PJ_SUCCESS) {
timerObj->Cancel();
delete timerObj;
return status;
}
++ht->cur_size;
return PJ_SUCCESS;
}
@ -251,13 +396,15 @@ PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
{
PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
if (entry->_timer_id != NULL) {
CPjTimerEntry *timerObj = (CPjTimerEntry*) entry->_timer_id;
timerObj->Cancel();
delete timerObj;
entry->_timer_id = NULL;
--ht->cur_size;
return 1;
if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) {
CPjTimerEntry *timerObj = ht->entries[entry->_timer_id];
if (timerObj) {
timerObj->Cancel();
delete timerObj;
return 1;
} else {
return 0;
}
} else {
return 0;
}