blob: 742b9e5d4f8dcf600a6c9cfaef0b3bbb147df0cf [file] [log] [blame]
Benny Prijonob2c96822007-05-03 13:31:21 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonob2c96822007-05-03 13:31:21 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pj/timer.h>
21#include <pj/pool.h>
22#include <pj/assert.h>
23#include <pj/errno.h>
24#include <pj/lock.h>
25
26#include "os_symbian.h"
27
28
29#define DEFAULT_MAX_TIMED_OUT_PER_POLL (64)
30
Benny Prijono8db270b2009-11-04 04:24:33 +000031// Maximum number of miliseconds that RTimer.At() supports
32#define MAX_RTIMER_INTERVAL 2147
33
Benny Prijonod53ed172009-12-16 13:30:34 +000034/* 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;
Benny Prijonob2c96822007-05-03 13:31:21 +000044
45/**
46 * The implementation of timer heap.
47 */
48struct pj_timer_heap_t
49{
50 /** Maximum size of the heap. */
51 pj_size_t max_size;
52
53 /** Current size of the heap. */
54 pj_size_t cur_size;
55
Benny Prijonod53ed172009-12-16 13:30:34 +000056 /** 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;
Benny Prijonob2c96822007-05-03 13:31:21 +000063};
64
Benny Prijonob2c96822007-05-03 13:31:21 +000065/**
66 * Active object for each timer entry.
67 */
68class CPjTimerEntry : public CActive
69{
70public:
Benny Prijonod53ed172009-12-16 13:30:34 +000071 pj_timer_entry *entry_;
72
Benny Prijonob2c96822007-05-03 13:31:21 +000073 static CPjTimerEntry* NewL( pj_timer_heap_t *timer_heap,
74 pj_timer_entry *entry,
75 const pj_time_val *delay);
76
77 ~CPjTimerEntry();
78
79 virtual void RunL();
80 virtual void DoCancel();
81
82private:
83 pj_timer_heap_t *timer_heap_;
Benny Prijonob2c96822007-05-03 13:31:21 +000084 RTimer rtimer_;
Benny Prijono8db270b2009-11-04 04:24:33 +000085 pj_uint32_t interval_left_;
Benny Prijonob2c96822007-05-03 13:31:21 +000086
87 CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
88 void ConstructL(const pj_time_val *delay);
Benny Prijono8db270b2009-11-04 04:24:33 +000089 void Schedule();
Benny Prijonob2c96822007-05-03 13:31:21 +000090};
91
Benny Prijonod53ed172009-12-16 13:30:34 +000092//////////////////////////////////////////////////////////////////////////////
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
Benny Prijonob2c96822007-05-03 13:31:21 +0000193
194CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap,
195 pj_timer_entry *entry)
Benny Prijonod53ed172009-12-16 13:30:34 +0000196: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap),
Benny Prijono8db270b2009-11-04 04:24:33 +0000197 interval_left_(0)
Benny Prijonob2c96822007-05-03 13:31:21 +0000198{
199}
200
201CPjTimerEntry::~CPjTimerEntry()
202{
Nanang Izzuddin82f7a412008-12-17 11:36:22 +0000203 Cancel();
Benny Prijonob2c96822007-05-03 13:31:21 +0000204 rtimer_.Close();
205}
206
Benny Prijono8db270b2009-11-04 04:24:33 +0000207void CPjTimerEntry::Schedule()
208{
209 pj_int32_t interval;
210
211 if (interval_left_ > MAX_RTIMER_INTERVAL) {
212 interval = MAX_RTIMER_INTERVAL;
213 } else {
214 interval = interval_left_;
215 }
216
217 interval_left_ -= interval;
218 rtimer_.After(iStatus, interval * 1000);
219 SetActive();
220}
221
Benny Prijonob2c96822007-05-03 13:31:21 +0000222void CPjTimerEntry::ConstructL(const pj_time_val *delay)
223{
224 rtimer_.CreateLocal();
225 CActiveScheduler::Add(this);
226
Benny Prijono8db270b2009-11-04 04:24:33 +0000227 interval_left_ = PJ_TIME_VAL_MSEC(*delay);
228 Schedule();
Benny Prijonob2c96822007-05-03 13:31:21 +0000229}
230
231CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap,
232 pj_timer_entry *entry,
233 const pj_time_val *delay)
234{
235 CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry);
236 CleanupStack::PushL(self);
237 self->ConstructL(delay);
238 CleanupStack::Pop(self);
239
240 return self;
241}
242
243void CPjTimerEntry::RunL()
244{
Benny Prijono8db270b2009-11-04 04:24:33 +0000245 if (interval_left_ > 0) {
246 Schedule();
247 return;
248 }
249
Benny Prijonod53ed172009-12-16 13:30:34 +0000250 remove_entry(timer_heap_, this);
Benny Prijonob2c96822007-05-03 13:31:21 +0000251 entry_->cb(timer_heap_, entry_);
252
253 // Finger's crossed!
254 delete this;
255}
256
257void CPjTimerEntry::DoCancel()
258{
Benny Prijonod53ed172009-12-16 13:30:34 +0000259 /* 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();
Benny Prijonob2c96822007-05-03 13:31:21 +0000267}
268
269
270//////////////////////////////////////////////////////////////////////////////
271
272
273/*
274 * Calculate memory size required to create a timer heap.
275 */
276PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
277{
278 return /* size of the timer heap itself: */
279 sizeof(pj_timer_heap_t) +
280 /* size of each entry: */
Benny Prijonod53ed172009-12-16 13:30:34 +0000281 (count+2) * (sizeof(void*)+sizeof(int)) +
Benny Prijonob2c96822007-05-03 13:31:21 +0000282 /* lock, pool etc: */
283 132;
284}
285
286/*
287 * Create a new timer heap.
288 */
289PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
290 pj_size_t size,
291 pj_timer_heap_t **p_heap)
292{
293 pj_timer_heap_t *ht;
Benny Prijonod53ed172009-12-16 13:30:34 +0000294 pj_status_t status;
Benny Prijonob2c96822007-05-03 13:31:21 +0000295
296 PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
297
298 *p_heap = NULL;
299
300 /* Allocate timer heap data structure from the pool */
Benny Prijonod53ed172009-12-16 13:30:34 +0000301 ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
Benny Prijonob2c96822007-05-03 13:31:21 +0000302 if (!ht)
303 return PJ_ENOMEM;
304
Benny Prijonod53ed172009-12-16 13:30:34 +0000305 /* Allocate slots */
306 status = realloc_timer_heap(ht, size);
307 if (status != PJ_SUCCESS)
308 return status;
Benny Prijonob2c96822007-05-03 13:31:21 +0000309
310 *p_heap = ht;
311 return PJ_SUCCESS;
312}
313
314PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
315{
Benny Prijonod53ed172009-12-16 13:30:34 +0000316 /* 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]) {
Benny Prijonoa1bc0192010-01-27 07:28:53 +0000321 ht->entries[i]->entry_ = NULL;
Benny Prijonod53ed172009-12-16 13:30:34 +0000322 ht->entries[i]->Cancel();
323 delete ht->entries[i];
324 ht->entries[i] = NULL;
325 }
326 }
327 }
328
329 delete [] ht->entries;
330 delete [] ht->free_slots;
331
332 ht->entries = NULL;
333 ht->free_slots = NULL;
Benny Prijonob2c96822007-05-03 13:31:21 +0000334}
335
336PJ_DEF(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
337 pj_lock_t *lock,
338 pj_bool_t auto_del )
339{
340 PJ_UNUSED_ARG(ht);
341 if (auto_del)
342 pj_lock_destroy(lock);
343}
344
345
346PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
347 unsigned count )
348{
Benny Prijonod53ed172009-12-16 13:30:34 +0000349 /* Not applicable */
350 PJ_UNUSED_ARG(count);
351 return ht->max_size;
Benny Prijonob2c96822007-05-03 13:31:21 +0000352}
353
354PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
355 int id,
356 void *user_data,
357 pj_timer_heap_callback *cb )
358{
359 pj_assert(entry && cb);
360
Benny Prijonod53ed172009-12-16 13:30:34 +0000361 entry->_timer_id = -1;
Benny Prijonob2c96822007-05-03 13:31:21 +0000362 entry->id = id;
363 entry->user_data = user_data;
364 entry->cb = cb;
365
366 return entry;
367}
368
369PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
370 pj_timer_entry *entry,
371 const pj_time_val *delay)
372{
373 CPjTimerEntry *timerObj;
Benny Prijonod53ed172009-12-16 13:30:34 +0000374 pj_status_t status;
Benny Prijonob2c96822007-05-03 13:31:21 +0000375
376 PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
377 PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
378
379 /* Prevent same entry from being scheduled more than once */
Benny Prijonod53ed172009-12-16 13:30:34 +0000380 PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
Benny Prijonob2c96822007-05-03 13:31:21 +0000381
Benny Prijonod53ed172009-12-16 13:30:34 +0000382 entry->_timer_id = -1;
Benny Prijonob2c96822007-05-03 13:31:21 +0000383
Benny Prijonod53ed172009-12-16 13:30:34 +0000384 timerObj = CPjTimerEntry::NewL(ht, entry, delay);
385 status = add_entry(ht, timerObj);
386 if (status != PJ_SUCCESS) {
387 timerObj->Cancel();
388 delete timerObj;
389 return status;
390 }
391
Benny Prijonob2c96822007-05-03 13:31:21 +0000392 return PJ_SUCCESS;
393}
394
395PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
396 pj_timer_entry *entry)
397{
398 PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
399
Benny Prijonod53ed172009-12-16 13:30:34 +0000400 if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) {
401 CPjTimerEntry *timerObj = ht->entries[entry->_timer_id];
402 if (timerObj) {
403 timerObj->Cancel();
404 delete timerObj;
405 return 1;
406 } else {
407 return 0;
408 }
Benny Prijonob2c96822007-05-03 13:31:21 +0000409 } else {
410 return 0;
411 }
412}
413
414PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
415 pj_time_val *next_delay )
416{
417 /* Polling is not necessary on Symbian, since all async activities
418 * are registered to active scheduler.
419 */
420 PJ_UNUSED_ARG(ht);
421 if (next_delay) {
422 next_delay->sec = 1;
423 next_delay->msec = 0;
424 }
425 return 0;
426}
427
428PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
429{
430 PJ_ASSERT_RETURN(ht, 0);
431
432 return ht->cur_size;
433}
434
435PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
436 pj_time_val *timeval)
437{
438 /* We don't support this! */
439 PJ_UNUSED_ARG(ht);
440
441 timeval->sec = 1;
442 timeval->msec = 0;
443
444 return PJ_SUCCESS;
445}
446