123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <pj/timer.h>
- #include <pj/pool.h>
- #include <pj/assert.h>
- #include <pj/errno.h>
- #include <pj/lock.h>
- #include "os_symbian.h"
- #define DEFAULT_MAX_TIMED_OUT_PER_POLL (64)
- // 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.
- */
- struct pj_timer_heap_t
- {
- /** Maximum size of the heap. */
- pj_size_t max_size;
- /** Current size of the heap. */
- pj_size_t cur_size;
- /** 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);
-
- ~CPjTimerEntry();
-
- virtual void RunL();
- virtual void DoCancel();
- private:
- pj_timer_heap_t *timer_heap_;
- RTimer rtimer_;
- pj_uint32_t interval_left_;
-
- CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
- void ConstructL(const pj_time_val *delay);
- 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), entry_(entry), timer_heap_(timer_heap),
- interval_left_(0)
- {
- }
- CPjTimerEntry::~CPjTimerEntry()
- {
- Cancel();
- rtimer_.Close();
- }
- void CPjTimerEntry::Schedule()
- {
- pj_int32_t interval;
-
- if (interval_left_ > MAX_RTIMER_INTERVAL) {
- interval = MAX_RTIMER_INTERVAL;
- } else {
- interval = interval_left_;
- }
-
- interval_left_ -= interval;
- rtimer_.After(iStatus, interval * 1000);
- SetActive();
- }
- void CPjTimerEntry::ConstructL(const pj_time_val *delay)
- {
- rtimer_.CreateLocal();
- CActiveScheduler::Add(this);
-
- interval_left_ = PJ_TIME_VAL_MSEC(*delay);
- Schedule();
- }
- CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap,
- pj_timer_entry *entry,
- const pj_time_val *delay)
- {
- CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry);
- CleanupStack::PushL(self);
- self->ConstructL(delay);
- CleanupStack::Pop(self);
- return self;
- }
- void CPjTimerEntry::RunL()
- {
- if (interval_left_ > 0) {
- Schedule();
- return;
- }
-
- remove_entry(timer_heap_, this);
- entry_->cb(timer_heap_, entry_);
-
- // Finger's crossed!
- delete this;
- }
- void CPjTimerEntry::DoCancel()
- {
- /* 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();
- }
- //////////////////////////////////////////////////////////////////////////////
- /*
- * Calculate memory size required to create a timer heap.
- */
- 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(void*)+sizeof(int)) +
- /* lock, pool etc: */
- 132;
- }
- /*
- * Create a new timer heap.
- */
- PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
- pj_size_t size,
- 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_ZALLOC_T(pool, pj_timer_heap_t);
- if (!ht)
- return PJ_ENOMEM;
- /* Allocate slots */
- status = realloc_timer_heap(ht, size);
- if (status != PJ_SUCCESS)
- return status;
- *p_heap = ht;
- return PJ_SUCCESS;
- }
- PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *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]->entry_ = NULL;
- 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,
- pj_lock_t *lock,
- pj_bool_t auto_del )
- {
- PJ_UNUSED_ARG(ht);
- if (auto_del)
- pj_lock_destroy(lock);
- }
- PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
- unsigned count )
- {
- /* Not applicable */
- PJ_UNUSED_ARG(count);
- return ht->max_size;
- }
- PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
- int id,
- void *user_data,
- pj_timer_heap_callback *cb )
- {
- pj_assert(entry && cb);
- entry->_timer_id = -1;
- entry->id = id;
- entry->user_data = user_data;
- entry->cb = cb;
- return entry;
- }
- PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- 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 < 1, PJ_EINVALIDOP);
- 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;
- }
-
- return PJ_SUCCESS;
- }
- PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- const pj_time_val *delay,
- int id_val,
- pj_grp_lock_t *grp_lock)
- {
- pj_status_t status;
-
- PJ_UNUSED_ARG(grp_lock);
- status = pj_timer_heap_schedule(ht, entry, delay);
-
- if (status == PJ_SUCCESS)
- entry->id = id_val;
-
- return status;
- }
- PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
- pj_timer_entry *entry)
- {
- PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
-
- 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;
- }
- }
- PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
- pj_timer_entry *entry,
- int id_val)
- {
- int count = pj_timer_heap_cancel(ht, entry);
- if (count == 1)
- entry->id = id_val;
-
- return count;
- }
- PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
- pj_time_val *next_delay )
- {
- /* Polling is not necessary on Symbian, since all async activities
- * are registered to active scheduler.
- */
- PJ_UNUSED_ARG(ht);
- if (next_delay) {
- next_delay->sec = 1;
- next_delay->msec = 0;
- }
- return 0;
- }
- PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
- {
- PJ_ASSERT_RETURN(ht, 0);
- return ht->cur_size;
- }
- PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
- pj_time_val *timeval)
- {
- /* We don't support this! */
- PJ_UNUSED_ARG(ht);
-
- timeval->sec = 1;
- timeval->msec = 0;
-
- return PJ_SUCCESS;
- }
|