blob: 4b1d61ba41aeb2ffa1bc661f0ecc9552dfcb727a [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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
31// Maximum number of miliseconds that RTimer.At() supports
32#define MAX_RTIMER_INTERVAL 2147
33
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;
44
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
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;
63};
64
65/**
66 * Active object for each timer entry.
67 */
68class CPjTimerEntry : public CActive
69{
70public:
71 pj_timer_entry *entry_;
72
73 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_;
84 RTimer rtimer_;
85 pj_uint32_t interval_left_;
86
87 CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
88 void ConstructL(const pj_time_val *delay);
89 void Schedule();
90};
91
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
193
194CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap,
195 pj_timer_entry *entry)
196: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap),
197 interval_left_(0)
198{
199}
200
201CPjTimerEntry::~CPjTimerEntry()
202{
203 Cancel();
204 rtimer_.Close();
205}
206
207void 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
222void CPjTimerEntry::ConstructL(const pj_time_val *delay)
223{
224 rtimer_.CreateLocal();
225 CActiveScheduler::Add(this);
226
227 interval_left_ = PJ_TIME_VAL_MSEC(*delay);
228 Schedule();
229}
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{
245 if (interval_left_ > 0) {
246 Schedule();
247 return;
248 }
249
250 remove_entry(timer_heap_, this);
251 entry_->cb(timer_heap_, entry_);
252
253 // Finger's crossed!
254 delete this;
255}
256
257void CPjTimerEntry::DoCancel()
258{
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();
267}
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: */
281 (count+2) * (sizeof(void*)+sizeof(int)) +
282 /* 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;
294 pj_status_t status;
295
296 PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
297
298 *p_heap = NULL;
299
300 /* Allocate timer heap data structure from the pool */
301 ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
302 if (!ht)
303 return PJ_ENOMEM;
304
305 /* Allocate slots */
306 status = realloc_timer_heap(ht, size);
307 if (status != PJ_SUCCESS)
308 return status;
309
310 *p_heap = ht;
311 return PJ_SUCCESS;
312}
313
314PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
315{
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]->entry_ = NULL;
322 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;
334}
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{
349 /* Not applicable */
350 PJ_UNUSED_ARG(count);
351 return ht->max_size;
352}
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
361 entry->_timer_id = -1;
362 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;
374 pj_status_t status;
375
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 */
380 PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
381
382 entry->_timer_id = -1;
383
384 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
392 return PJ_SUCCESS;
393}
394
395PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
396 pj_timer_entry *entry,
397 const pj_time_val *delay,
398 int id_val,
399 pj_grp_lock_t *grp_lock)
400{
401 pj_status_t status;
402
403 PJ_UNUSED_ARG(grp_lock);
404
405 status = pj_timer_heap_schedule(ht, entry, delay);
406
407 if (status == PJ_SUCCESS)
408 entry->id = id_val;
409
410 return status;
411}
412
413PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
414 pj_timer_entry *entry)
415{
416 PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
417
418 if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) {
419 CPjTimerEntry *timerObj = ht->entries[entry->_timer_id];
420 if (timerObj) {
421 timerObj->Cancel();
422 delete timerObj;
423 return 1;
424 } else {
425 return 0;
426 }
427 } else {
428 return 0;
429 }
430}
431
432PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
433 pj_timer_entry *entry,
434 int id_val)
435{
436 int count = pj_timer_heap_cancel(ht, entry);
437 if (count == 1)
438 entry->id = id_val;
439
440 return count;
441}
442
443PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht,
444 pj_time_val *next_delay )
445{
446 /* Polling is not necessary on Symbian, since all async activities
447 * are registered to active scheduler.
448 */
449 PJ_UNUSED_ARG(ht);
450 if (next_delay) {
451 next_delay->sec = 1;
452 next_delay->msec = 0;
453 }
454 return 0;
455}
456
457PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
458{
459 PJ_ASSERT_RETURN(ht, 0);
460
461 return ht->cur_size;
462}
463
464PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
465 pj_time_val *timeval)
466{
467 /* We don't support this! */
468 PJ_UNUSED_ARG(ht);
469
470 timeval->sec = 1;
471 timeval->msec = 0;
472
473 return PJ_SUCCESS;
474}
475