MHEG5  18.9.0
MHEG5 Documentation
glue_timers.c
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright © 2014 The DTVKit Open Software Foundation Ltd (www.dtvkit.org)
3  * Copyright © 2004 Ocean Blue Software Ltd
4  * Copyright © 2000 Koninklijke Philips Electronics N.V
5  *
6  * This file is part of a DTVKit Software Component
7  * You are permitted to copy, modify or distribute this file subject to the terms
8  * of the DTVKit 1.0 Licence which can be found in licence.txt or at www.dtvkit.org
9  *
10  * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
11  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
12  * OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
13  *
14  * If you or your organisation is not a member of DTVKit then you have access
15  * to this source code outside of the terms of the licence agreement
16  * and you are expected to delete this and any associated files immediately.
17  * Further information on DTVKit, membership and terms can be found at www.dtvkit.org
18  *******************************************************************************/
26 /*---includes for this file--------------------------------------------------*/
27 #include <stdlib.h>
28 #include "stb_os.h"
29 #include "glue_memory.h"
30 #include "glue_debug.h"
31 #include "glue_timers.h"
32 
33 /*---constant definitions for this file--------------------------------------*/
34 
35 /* Some magic numbers which are set in the timer info structure to help detect
36  * for invalid handles
37  */
38 #define MHEG_TIMER_MAGIC 0xc10c0000
39 
40 /* Maximum wait of the OSAL is 0x7fffffff ms. We specify a maximum timeout of 1
41  * minute to try to recover from programming errors.
42  */
43 #define MHEG_TIMER_MAX_WAIT 60000
44 
45 /* Define the accuracy with which we expect timers to fire in milliseconds. They
46  * may fire up to this many milliseconds early.
47  *
48  * NOTE: A problem with dvp1 beta2 nt4 build was found. A timeout of 1 millisec
49  * appears to wait indefinately, so this value should be 2 or greater.
50  */
51 #define MHEG_TIMER_ACCURACY 5
52 
53 #define SIZEOF_TIMER_EVENT_MSG 1
54 #define MAX_NUM_TIMER_EVENT_MSG 100
55 
56 
57 /*---local typedef structs for this file-------------------------------------*/
58 
59 /* Fill in the structure that an H_Timer points to. This has the
60  * following members:
61  * timerHdl Handle/Identity (with magic number for validating)
62  * triggerTime Absolute system time that the timer will trigger
63  * callback Pointer to the callback function that will be invoked
64  * when the timer expires or is destroyed when unexpired.
65  * callerRef Caller reference that will be passed to the callback
66  * next Pointer to next timer in the timer list.
67  */
68 typedef struct s_timer_info
69 {
73  void *callerRef;
74  struct s_timer_info *next;
75 } S_TIMER_INFO;
76 
77 /*---local (static) variable declarations for this file----------------------*/
78 
79 /* The timerListHead stores a pointer to the first timer in a linked list of
80  * active timers. The list is always stored such that the next timer to fire
81  * is at the head of the list.
82  */
83 static S_TIMER_INFO *timerListHead = 0;
84 
85 /* Handle of a mutex used to protect against simultaneous access of the list
86  * of timers. Timer data that is on the list must not be read or written without
87  * holding the mutex.
88  */
89 static void *mhegTimerListMutex = 0;
90 static void *mhegTimerCountMutex = 0;
91 
92 /* Handle of an event that is used to signal to the timer task that the head
93  * of the timer list has changed. This event causes the main task timeout to be
94  * re-evaluated. It is also used to wake the main task when we want it to die.
95  */
96 static void *mhegTimerUpdateEvent = 0;
97 
98 /* Handle of the timer task */
99 static void *mhegTimerTaskHandle = 0;
100 
101 /* Set to TRUE to indicate that we want the main timer task to exit.
102  */
103 static BOOLEAN mhegTimerQuit = FALSE;
104 
105 /* Set to TRUE to indicate that the timer task is running.
106  */
107 static BOOLEAN mhegTimerTaskRunning = FALSE;
108 
109 /* Basis of timer identity - to be combined with magic number to create handle */
110 static U16BIT mhegTimerIdentityCount = 0;
111 
112 
113 /*---local function definitions----------------------------------------------*/
114 
115 /*---global function definitions---------------------------------------------*/
116 
117 
128 static void TimersMain(void *pArg)
129 {
130  S32BIT eventWaitTimeout = 0;
131  U32BIT sysTime;
132 
133  U8BIT timerEventMsg;
134 
135  /* Show that the timer task is running */
136  mhegTimerTaskRunning = TRUE;
137 
138  while (!mhegTimerQuit)
139  {
140  /* Get the current system time */
141  sysTime = STB_OSGetClockMilliseconds();
142 
143  /* Adjust the system time to allow for a margin in the timeout. */
144  sysTime += MHEG_TIMER_ACCURACY;
145 
146  /* Enter the mutex before we read/write the timer list */
147  STB_OSMutexLock(mhegTimerListMutex);
148 
149  /* Check for events that need to fire */
150  while ((timerListHead != 0) && (timerListHead->triggerTime <= sysTime))
151  {
152  S_TIMER_INFO *pOldTimerInfo;
153 
154  /* Keep a reference to this timer */
155  pOldTimerInfo = timerListHead;
156 
157  /* Remove this timer from the list (it is at the head of the
158  * list)
159  */
160 
161  timerListHead = timerListHead->next;
162 
163  /* Call the callback for this timer. */
164  (pOldTimerInfo->callback)( TRUE, pOldTimerInfo->callerRef,
165  (H_Timer)pOldTimerInfo->timerHdl.ptr);
166 
167  /* Free the memory. */
168  SYS_Free( pOldTimerInfo );
169  }
170 
171  /* Calculate the timeout based on the first entry in the timer list */
172  if (timerListHead == 0)
173  {
174  /* No entries left in timer list. Set out timeout variable to a
175  * large value. We expect the task to be woken by an event (when
176  * a new timer is added).
177  */
178  eventWaitTimeout = MHEG_TIMER_MAX_WAIT;
179  }
180  else
181  {
182  /* Calculate the number of milliseconds until the next timer fires
183  * and use this as the timout for the event wait.
184  */
185  eventWaitTimeout = timerListHead->triggerTime - sysTime;
186 
187  /* Cap the event wait timeout to the max allowed value. */
188  if (eventWaitTimeout > MHEG_TIMER_MAX_WAIT)
189  {
190  eventWaitTimeout = MHEG_TIMER_MAX_WAIT;
191  }
192  }
193 
194  /* We have finished with the timer list, so release the mutex */
195  STB_OSMutexUnlock(mhegTimerListMutex);
196 
197  /* Wait for either:
198  * a) an event, indicating that a new entry has been added to the head
199  * of the list of timers.
200  * b) a timeout, indicating that the first timer entry should be fired
201  */
202  STB_OSReadQueue(mhegTimerUpdateEvent, (void *) &timerEventMsg, SIZEOF_TIMER_EVENT_MSG, (U16BIT) eventWaitTimeout);
203  }
204 
205  /* Show that timer task is exiting */
206  mhegTimerTaskRunning = FALSE;
207 }
208 
225  H_Timer *pTimerHandle)
226 {
227  U32BIT sysTime;
228  S_TIMER_INFO *pNewTimerInfo;
229 
230  U8BIT timerEventMsg;
231 
232  /* Initialise returned handle to 0 */
233  *pTimerHandle = 0;
234 
235  /* Get the current system time */
236  sysTime = STB_OSGetClockMilliseconds();
237 
238  if (!mhegTimerCountMutex || !mhegTimerListMutex)
239  {
240  return MHERR_COMP_NOT_STARTED;
241  }
242 
243  /* Allocate memory for storing the timer */
244  pNewTimerInfo = SYS_Alloc(sizeof(S_TIMER_INFO));
245  if (pNewTimerInfo == 0)
246  {
248  }
249 
250  /* Add the relative time to the system time, detecting a wrap of the least
251  * significant bits
252  */
253  pNewTimerInfo->triggerTime = sysTime + millisecs;
254 
255  pNewTimerInfo->callback = callback;
256  pNewTimerInfo->callerRef = callerRef;
257 
258  /* We now need to insert the newly created entry into the linked list.
259  * The linked list is kept in triggering order, so we need to insert the
260  * new entry in the appropriate location in the list. If we add the new
261  * timer to the head of the list we send an event to the main timer task
262  * so that it can adjust it's timeout value.
263  */
264 
265  /* Enter the mutex before we read/write the timer list */
266  STB_OSMutexLock(mhegTimerCountMutex);
267  ++mhegTimerIdentityCount;
268  pNewTimerInfo->timerHdl.u32 = MHEG_TIMER_MAGIC | mhegTimerIdentityCount;
269  STB_OSMutexUnlock(mhegTimerCountMutex);
270 
271  /* Pass the new timer handle back to the caller */
272  *pTimerHandle = (H_Timer)pNewTimerInfo->timerHdl.ptr;
273 
274  STB_OSMutexLock(mhegTimerListMutex);
275  if ((timerListHead == 0) ||
276  (timerListHead->triggerTime >= pNewTimerInfo->triggerTime))
277  {
278  /* Either there are no entries in the list, or the first entry in the
279  * list triggers after the new one. Add the new timer to the head of the
280  * list
281  */
282  pNewTimerInfo->next = timerListHead;
283  timerListHead = pNewTimerInfo;
284 
285  /* Notify the timer thread that the head of the list has changed. This
286  * causes the timout to be re-evaluated.
287  */
288  STB_OSWriteQueue(mhegTimerUpdateEvent, (void *) &timerEventMsg, SIZEOF_TIMER_EVENT_MSG, TIMEOUT_NOW);
289  }
290  else
291  {
292  S_TIMER_INFO *pTimerInfo;
293 
294  /* Search through the list while
295  * a) we are not at the end of the list
296  * b) the next timer triggers before the new one
297  */
298  pTimerInfo = timerListHead;
299  while ((pTimerInfo->next != 0) &&
300  (pTimerInfo->next->triggerTime <= pNewTimerInfo->triggerTime))
301  {
302  pTimerInfo = pTimerInfo->next;
303  }
304 
305  /* Insert the new timer into this position in the list */
306  pNewTimerInfo->next = pTimerInfo->next;
307  pTimerInfo->next = pNewTimerInfo;
308  }
309 
310  /* We have finished with the timer list, so release the mutex */
311  STB_OSMutexUnlock(mhegTimerListMutex);
312 
313  return MHERR_OK;
314 }
315 
325 {
327  S_TIMER_INFO *pOldTimerInfo = NULL;
328  E_MhegErr retVal;
329 
330  timerHdl.ptr = timerHandle;
331  assert((timerHdl.u32 & 0xffff0000) == MHEG_TIMER_MAGIC);
332 
333  if (mhegTimerListMutex == 0)
334  {
335  retVal = MHERR_COMP_NOT_OPEN;
336  }
337  else
338  {
339  /* Enter the mutex before we read/write the timer list */
340  STB_OSMutexLock(mhegTimerListMutex);
341 
342  /* Check the handle that we have been passed */
343  if (timerListHead == NULL)
344  {
345  retVal = MHERR_BAD_PARAMETER;
346  }
347  else
348  {
349  if (timerListHead->timerHdl.ptr == timerHdl.ptr)
350  {
351  pOldTimerInfo = timerListHead;
352  timerListHead = timerListHead->next;
353  }
354  else
355  {
356  S_TIMER_INFO *pTimerInfo;
357  S_TIMER_INFO *prevTimerInfo;
358  prevTimerInfo = timerListHead;
359  pTimerInfo = timerListHead->next;
360  while (pTimerInfo != 0)
361  {
362  if (pTimerInfo->timerHdl.ptr == timerHdl.ptr)
363  {
364  pOldTimerInfo = pTimerInfo;
365  prevTimerInfo->next = pTimerInfo->next;
366  break;
367  }
368  prevTimerInfo = pTimerInfo;
369  pTimerInfo = pTimerInfo->next;
370  }
371  }
372 
373  if (pOldTimerInfo)
374  {
375  /* The timer was still in the active timer list. Call the callback
376  * to allow the calling application to free of any resource for this
377  * timer.
378  * NOTE: The triggered flag is set to FALSE.
379  */
380  (pOldTimerInfo->callback)( FALSE, pOldTimerInfo->callerRef, (H_Timer)timerHdl.ptr);
381 
382  /* Free the memory. */
383  SYS_Free(pOldTimerInfo);
384  retVal = MHERR_OK;
385  }
386  else
387  {
388  retVal = MHERR_BAD_PARAMETER;
389  }
390  }
391 
392  /* We have finished with the timer list, so release the mutex */
393  STB_OSMutexUnlock(mhegTimerListMutex);
394  }
395 
396  return retVal;
397 }
398 
408 {
409  if (mhegTimerTaskHandle != 0 && mhegTimerListMutex != 0)
410  {
411  /* aleady initialised - just say it is OK */
412  return MHERR_OK;
413  }
414  else
415  {
416  /* Create the mutex for ensuring integrity of the timer list */
417  mhegTimerListMutex = STB_OSCreateMutex();
418  TRACE(TKEYS, ("mhegTimerListMutex=%d", mhegTimerListMutex))
419  if (mhegTimerListMutex == NULL)
420  {
422  }
423  mhegTimerCountMutex = STB_OSCreateMutex();
424  TRACE(TKEYS, ("mhegTimerCountMutex=%d", mhegTimerCountMutex))
425  if (mhegTimerCountMutex == NULL)
426  {
427  STB_OSDeleteMutex(mhegTimerListMutex);
428  mhegTimerListMutex = 0;
430  }
431 
432  /* Create the event used to notify the main task of a change to the head of
433  * the timer list
434  */
436  if (mhegTimerUpdateEvent == NULL)
437  {
438  STB_OSDeleteMutex(mhegTimerCountMutex);
439  mhegTimerCountMutex = 0;
440  STB_OSDeleteMutex(mhegTimerListMutex);
441  mhegTimerListMutex = 0;
443  }
444 
445  /*timer priority should be higher than the mheg engine*/
446  task_priority += 2;
447 
448  /* Clear the quit flag so that the task can start */
449  mhegTimerQuit = FALSE;
450 
451  /* Create the main timer task */
452  mhegTimerTaskHandle = STB_OSCreateTask( TimersMain, NULL, 0x1000, (U8BIT)task_priority, (U8BIT *)"MhgTimers" );
453  if (mhegTimerTaskHandle == NULL)
454  {
455  STB_OSDestroyQueue( mhegTimerUpdateEvent );
456  mhegTimerUpdateEvent = 0;
457  STB_OSDeleteMutex(mhegTimerCountMutex);
458  mhegTimerCountMutex = 0;
459  STB_OSDeleteMutex(mhegTimerListMutex);
460  mhegTimerListMutex = 0;
462  }
463  }
464  return MHERR_OK;
465 }
466 
475 {
476  U8BIT timerEventMsg;
477 
478  /* Check that we are initialised */
479  if (!mhegTimerTaskHandle)
480  {
481  return MHERR_COMP_NOT_OPEN;
482  }
483 
484  /* Signal to the timer task that we want it to die. */
485  mhegTimerQuit = TRUE;
486 
487  /* If the task is running kill it off. */
488  if (mhegTimerTaskRunning)
489  {
490  STB_OSWriteQueue(mhegTimerUpdateEvent, (void *) &timerEventMsg, SIZEOF_TIMER_EVENT_MSG, TIMEOUT_NOW);
491 
492  /* Wait for the timer task to die. */
493  while (mhegTimerTaskRunning)
494  {
495  /* Sleep for 10 ms */
496  STB_OSTaskDelay(10);
497  }
498  STB_OSDestroyTask(mhegTimerTaskHandle);
499  mhegTimerTaskHandle = 0;
500  STB_OSDestroyQueue(mhegTimerUpdateEvent);
501  mhegTimerUpdateEvent = NULL;
502  }
503 
504  /* Destroy all resources */
505  if (mhegTimerListMutex != 0)
506  {
507  STB_OSMutexLock(mhegTimerListMutex);
508  /* Free off any timers left in the timer list */
509  while (timerListHead != NULL)
510  {
511  S_TIMER_INFO *pTimerInfo;
512 
513  pTimerInfo = timerListHead;
514  timerListHead = timerListHead->next;
515  (pTimerInfo->callback)(FALSE, pTimerInfo->callerRef, (H_Timer)pTimerInfo->timerHdl.ptr);
516  SYS_Free(pTimerInfo);
517  }
518 
519  STB_OSMutexUnlock(mhegTimerListMutex);
520  STB_OSDeleteMutex(mhegTimerCountMutex);
521  mhegTimerCountMutex = 0;
522  STB_OSDeleteMutex(mhegTimerListMutex);
523  mhegTimerListMutex = 0;
524  }
525  return MHERR_OK;
526 }
527 
U32BIT STB_OSGetClockMilliseconds(void)
Get Current Computer Clock Time.
E_MhegErr VT_TimerDestroy(H_Timer timerHandle)
Destroy the specified timer. A callback will not be invoked for this timer after this function return...
Definition: glue_timers.c:324
void * ptr
Definition: glue_type.h:38
void * callerRef
Definition: glue_timers.c:73
void * H_Timer
Definition: glue_timers.h:58
U32BIT triggerTime
Definition: glue_timers.c:71
void STB_OSTaskDelay(U16BIT timeout)
Delay Task for Specifed Time Period.
#define MAX_NUM_TIMER_EVENT_MSG
Definition: glue_timers.c:54
E_MhegErr VT_TimerCreate(U32BIT millisecs, F_TimerCallback callback, void *callerRef, H_Timer *pTimerHandle)
Set a timer for a certain number of millseconds. When the timer expires a specified callback will be ...
Definition: glue_timers.c:223
Debug tracing.
void STB_OSDeleteMutex(void *mutex)
Delete a mutex.
struct s_timer_info S_TIMER_INFO
#define SYS_Free
Definition: glue_memory.h:85
E_MhegErr
Definition: mherrors.h:28
uint8_t U8BIT
Definition: techtype.h:82
void(* F_TimerCallback)(BOOLEAN triggered, void *callerRef, H_Timer timerHandle)
Definition: glue_timers.h:77
BOOLEAN STB_OSDestroyQueue(void *queue)
Destroy Queue.
BOOLEAN STB_OSWriteQueue(void *queue, void *msg, U16BIT msg_size, U16BIT timeout)
Write a message to the queue.
Memory functions.
void STB_OSMutexUnlock(void *mutex)
Unlock a mutex (a.k.a. &#39;leave&#39;, &#39;signal&#39; or &#39;release&#39;)
#define SIZEOF_TIMER_EVENT_MSG
Definition: glue_timers.c:53
void * STB_OSCreateQueue(U16BIT msg_size, U16BIT msg_max)
Create Queue of given number of messages and size of message.
U32BIT u32
Definition: glue_type.h:39
void * STB_OSCreateMutex(void)
Create a mutex.
F_TimerCallback callback
Definition: glue_timers.c:72
BOOLEAN STB_OSReadQueue(void *queue, void *msg, U16BIT msg_size, U16BIT timeout)
Read a message from a queue.
int32_t S32BIT
Definition: techtype.h:87
E_MhegErr VT_TimersExit(void)
Uninitialise the timer component - all resources are freed. Other timer functions must not be called ...
Definition: glue_timers.c:474
uint16_t U16BIT
Definition: techtype.h:84
struct s_timer_info * next
Definition: glue_timers.c:74
#define MHEG_TIMER_MAGIC
Definition: glue_timers.c:38
#define TIMEOUT_NOW
Definition: stb_os.h:33
#define MHEG_TIMER_ACCURACY
Definition: glue_timers.c:51
#define FALSE
Definition: techtype.h:68
void STB_OSMutexLock(void *mutex)
Lock a mutex (a.k.a. &#39;enter&#39;, &#39;wait&#39; or &#39;get&#39;).
The timer module allows the use of timers within the MHEG5 component. These timers can be set by othe...
U8BIT BOOLEAN
Definition: techtype.h:99
#define TRUE
Definition: techtype.h:69
U_PARAM timerHdl
Definition: glue_timers.c:70
E_MhegErr VT_TimersInit(U32BIT task_priority)
Initialise the timer component. This function must be called before any other timer functions are inv...
Definition: glue_timers.c:407
void * STB_OSCreateTask(void(*function)(void *), void *param, U32BIT stack, U8BIT priority, U8BIT *name)
Create a New Task to the calling process. Upon success, the created task runs on its own stack...
#define MHEG_TIMER_MAX_WAIT
Definition: glue_timers.c:43
uint32_t U32BIT
Definition: techtype.h:86
#define SYS_Alloc
Definition: glue_memory.h:84
void STB_OSDestroyTask(void *task)
Delete Task must be called upon termination of each task as it frees all OS specific resources alloca...
#define TRACE(t, x)
Definition: glue_debug.h:118
Header file - Function prototypes for operating system.