Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjlib / src / pj / timer_symbian.cpp
1 /* $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
43 class CPjTimerEntry;
44
45 /**
46  * The implementation of timer heap.
47  */
48 struct 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  */
68 class CPjTimerEntry : public CActive 
69 {
70 public:
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
82 private:        
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 */
98 static 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 */
149 static 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 */
176 static 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
194 CPjTimerEntry::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
201 CPjTimerEntry::~CPjTimerEntry() 
202 {
203     Cancel();
204     rtimer_.Close();
205 }
206
207 void 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
222 void 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
231 CPjTimerEntry* 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
243 void 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
257 void 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  */
276 PJ_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  */
289 PJ_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
314 PJ_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
336 PJ_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
346 PJ_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
354 PJ_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
369 PJ_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
395 PJ_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     
400     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         }
409     } else {
410         return 0;
411     }
412 }
413
414 PJ_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
428 PJ_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
435 PJ_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