source: rtems/cpukit/rtems/src/timerserver.c @ 1de98d97

4.104.115
Last change on this file since 1de98d97 was 1de98d97, checked in by Joel Sherrill <joel.sherrill@…>, on 12/19/08 at 16:32:33

2008-12-19 Joel Sherrill <joel.sherrill@…>

  • rtems/src/dpmemcreate.c: Spacing.
  • rtems/src/timerserver.c: Move error check to RTEMS_DEBUG. Cannot happen under normal circumstances.
  • Property mode set to 100644
File size: 14.9 KB
Line 
1/**
2 *  @file timerserver.c
3 *
4 *  Timer Manager - rtems_timer_initiate_server directive along with
5 *  the Timer Server Body and support routines
6 *
7 *  @note Data specific to the Timer Server is declared in this
8 *        file as the Timer Server so it does not have to be in the
9 *        minimum footprint.  It is only really required when
10 *        task-based timers are used.  Since task-based timers can
11 *        not be started until the server is initiated, this structure
12 *        does not have to be initialized until then.
13 */
14
15/*  COPYRIGHT (c) 1989-2008.
16 *  On-Line Applications Research Corporation (OAR).
17 *
18 *  The license and distribution terms for this file may be
19 *  found in the file LICENSE in this distribution or at
20 *  http://www.rtems.com/license/LICENSE.
21 *
22 *  $Id$
23 */
24
25#if HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <rtems/system.h>
30#include <rtems/rtems/status.h>
31#include <rtems/rtems/support.h>
32#include <rtems/score/object.h>
33#include <rtems/score/thread.h>
34#include <rtems/rtems/timer.h>
35#include <rtems/score/tod.h>
36#include <rtems/score/watchdog.h>
37
38#include <rtems/rtems/tasks.h>
39#include <rtems/rtems/support.h>
40#include <rtems/score/thread.h>
41
42/**
43 *  This chain contains the list of interval timers that are
44 *  executed in the context of the Timer Server.
45 */
46Chain_Control _Timer_Ticks_chain;
47
48/**
49 *  This chain contains the list of time of day timers that are
50 *  executed in the context of the Timer Server.
51 */
52Chain_Control _Timer_Seconds_chain;
53
54/**
55 *  This chain holds the set of timers to be inserted when the
56 *  server runs again.
57 */
58Chain_Control _Timer_To_be_inserted;
59
60/**
61 *  This variables keeps track of the last time the Timer Server actually
62 *  processed the ticks chain.
63 */
64Watchdog_Interval _Timer_Server_ticks_last_time;
65
66/**
67 *  This variable keeps track of the last time the Timer Server actually
68 *  processed the seconds chain.
69 */
70Watchdog_Interval _Timer_Server_seconds_last_time;
71
72/**
73 *  This is the timer used to control when the Timer Server wakes up to
74 *  service "when" timers.
75 *
76 *  @note The timer in the Timer Server TCB is used for ticks timer.
77 */
78Watchdog_Control _Timer_Seconds_timer;
79
80/**
81 *  This method is used to temporarily disable updates to the
82 *  Ticks Timer Chain managed by the Timer Server.
83 */
84#define _Timer_Server_stop_ticks_timer() \
85      _Watchdog_Remove( &_Timer_Server->Timer )
86
87/**
88 *  This method is used to temporarily disable updates to the
89 *  Seconds Timer Chain managed by the Timer Server.
90 */
91#define _Timer_Server_stop_seconds_timer() \
92      _Watchdog_Remove( &_Timer_Seconds_timer );
93
94/**
95 *  This method resets a timer and places it on the Ticks chain.  It
96 *  is assumed that the timer has already been canceled.
97 */
98#define _Timer_Server_reset_ticks_timer() \
99   do { \
100      if ( !_Chain_Is_empty( &_Timer_Ticks_chain ) ) { \
101        _Watchdog_Insert_ticks( &_Timer_Server->Timer, \
102           ((Watchdog_Control *)_Timer_Ticks_chain.first)->delta_interval ); \
103      } \
104   } while (0)
105
106/**
107 *  This method resets a timer and places it on the Seconds chain.  It
108 *  is assumed that the timer has already been canceled.
109 */
110#define _Timer_Server_reset_seconds_timer() \
111   do { \
112      if ( !_Chain_Is_empty( &_Timer_Seconds_chain ) ) { \
113        _Watchdog_Insert_seconds( &_Timer_Seconds_timer, \
114          ((Watchdog_Control *)_Timer_Seconds_chain.first)->delta_interval ); \
115      } \
116   } while (0)
117
118/**
119 *  @brief _Timer_Server_process_insertions
120 *
121 *  This method processes the set of timers scheduled for insertion
122 *  onto one of the Timer Server chains.
123 *
124 *  @note It is only to be called from the Timer Server task.
125 */
126static void _Timer_Server_process_insertions(void)
127{
128  Timer_Control *the_timer;
129
130  while ( 1 ) {
131    the_timer = (Timer_Control *) _Chain_Get( &_Timer_To_be_inserted );
132    if ( the_timer == NULL )
133      break;
134
135    if ( the_timer->the_class == TIMER_INTERVAL_ON_TASK ) {
136      _Watchdog_Insert( &_Timer_Ticks_chain, &the_timer->Ticker );
137    } else if ( the_timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
138      _Watchdog_Insert( &_Timer_Seconds_chain, &the_timer->Ticker );
139    }
140  }
141}
142
143/**
144 *  @brief _Timer_Server_process_ticks_chain
145 *
146 *  This routine is responsible for adjusting the list of task-based
147 *  interval timers to reflect the passage of time.
148 *
149 *  @param[in] to_fire will contain the set of timers that are to be fired.
150 *
151 *  @note It is only to be called from the Timer Server task.
152 */
153static void _Timer_Server_process_ticks_chain(
154  Chain_Control *to_fire
155)
156{
157  Watchdog_Interval snapshot;
158  Watchdog_Interval ticks;
159
160  snapshot = _Watchdog_Ticks_since_boot;
161  if ( snapshot >= _Timer_Server_ticks_last_time )
162     ticks = snapshot - _Timer_Server_ticks_last_time;
163  else
164     ticks = (0xFFFFFFFF - _Timer_Server_ticks_last_time) + snapshot;
165
166  _Timer_Server_ticks_last_time = snapshot;
167  _Watchdog_Adjust_to_chain( &_Timer_Ticks_chain, ticks, to_fire );
168}
169
170/**
171 *  @brief _Timer_Server_process_seconds_chain
172 *
173 *  This routine is responsible for adjusting the list of task-based
174 *  time of day timers to reflect the passage of time.
175 *
176 *  @param[in] to_fire will contain the set of timers that are to be fired.
177 *
178 *  @note It is only to be called from the Timer Server task.
179 */
180static void _Timer_Server_process_seconds_chain(
181  Chain_Control *to_fire
182)
183{
184  Watchdog_Interval snapshot;
185  Watchdog_Interval ticks;
186
187  /*
188   *  Process the seconds chain.  Start by checking that the Time
189   *  of Day (TOD) has not been set backwards.  If it has then
190   *  we want to adjust the _Timer_Seconds_chain to indicate this.
191   */
192  snapshot =  _TOD_Seconds_since_epoch();
193  if ( snapshot > _Timer_Server_seconds_last_time ) {
194    /*
195     *  This path is for normal forward movement and cases where the
196     *  TOD has been set forward.
197     */
198    ticks = snapshot - _Timer_Server_seconds_last_time;
199    _Watchdog_Adjust_to_chain( &_Timer_Seconds_chain, ticks, to_fire );
200
201  } else if ( snapshot < _Timer_Server_seconds_last_time ) {
202     /*
203      *  The current TOD is before the last TOD which indicates that
204      *  TOD has been set backwards.
205      */
206     ticks = _Timer_Server_seconds_last_time - snapshot;
207     _Watchdog_Adjust( &_Timer_Seconds_chain, WATCHDOG_BACKWARD, ticks );
208  }
209  _Timer_Server_seconds_last_time = snapshot;
210}
211
212/**
213 *  @brief _Timer_Server_body
214 *
215 *  This is the server for task based timers.  This task executes whenever
216 *  a task-based timer should fire.  It services both "after" and "when"
217 *  timers.  It is not created automatically but must be created explicitly
218 *  by the application before task-based timers may be initiated.
219 *
220 *  @param[in] ignored is the the task argument that is ignored
221 */
222Thread _Timer_Server_body(
223  uint32_t   ignored
224)
225{
226  Chain_Control to_fire;
227
228  _Chain_Initialize_empty( &to_fire );
229
230  /*
231   *  Initialize the "last time" markers to indicate the timer that
232   *  the server was initiated.
233   */
234  _Timer_Server_ticks_last_time   = _Watchdog_Ticks_since_boot;
235  _Timer_Server_seconds_last_time = _TOD_Seconds_since_epoch();
236
237  /*
238   *  Insert the timers that were inserted before we got to run.
239   *  This should be done with dispatching disabled.
240   */
241  _Thread_Disable_dispatch();
242    _Timer_Server_process_insertions();
243  _Thread_Enable_dispatch();
244
245  while(1) {
246
247    /*
248     *  Block until there is something to do.
249     */
250    _Thread_Disable_dispatch();
251      _Thread_Set_state( _Timer_Server, STATES_DELAYING );
252      _Timer_Server_reset_ticks_timer();
253      _Timer_Server_reset_seconds_timer();
254    _Thread_Enable_dispatch();
255
256    /********************************************************************
257     ********************************************************************
258     ****                TIMER SERVER BLOCKS HERE                    ****
259     ********************************************************************
260     ********************************************************************/
261
262    /*
263     *  Disable dispatching while processing the timers since we want
264     *  the removal of the timers from the chain to be atomic.
265     *
266     *  NOTE: Dispatching is disabled for interrupt based TSRs.
267     *        Dispatching is enabled for task based TSRs so they
268     *          can temporarily malloc memory or block.
269     *        _ISR_Nest_level is 0 for task-based TSRs and non-zero
270     *          for the others.
271     */
272    _Thread_Disable_dispatch();
273
274    /*
275     *  At this point, at least one of the timers this task relies
276     *  upon has fired.  Stop them both while we process any outstanding
277     *  timers.  Before we block, we will restart them.
278     */
279    _Timer_Server_stop_ticks_timer();
280    _Timer_Server_stop_seconds_timer();
281
282    /*
283     *  Remove all the timers that need to fire so we can invoke them
284     *  outside the critical section.
285     */
286    _Timer_Server_process_ticks_chain( &to_fire );
287    _Timer_Server_process_seconds_chain( &to_fire );
288
289    /*
290     *  Insert the timers that have been requested to be inserted.
291     */
292    _Timer_Server_process_insertions();
293
294    /*
295     * Enable dispatching to process the set that are ready "to fire."
296     */
297    _Thread_Enable_dispatch();
298
299    /*
300     *  Now we actually invoke the TSR for all the timers that fired.
301     *  This is done with dispatching
302     */
303    while (1) {
304      Watchdog_Control *watch;
305      ISR_Level         level;
306
307      _ISR_Disable( level );
308      watch = (Watchdog_Control *) _Chain_Get_unprotected( &to_fire );
309      if ( watch == NULL ) {
310        _ISR_Enable( level );
311        break;
312      }
313
314      watch->state = WATCHDOG_INACTIVE;
315      _ISR_Enable( level );
316
317      (*watch->routine)( watch->id, watch->user_data );
318    }
319  }
320
321  return 0;   /* unreached - only to remove warnings */
322}
323
324/**
325 *  This method schedules the insertion of timers on the proper list.  It
326 *  wakes up the Timer Server task to process the insertion.
327 *
328 *  @param[in] the_timer is the timer to insert
329 *
330 *  @note It is highly likely the executing task will be preempted after
331 *        the directive invoking this is executed.
332 */
333static void _Timer_Server_schedule_operation_method(
334  Timer_Control     *the_timer
335)
336{
337  _Chain_Append( &_Timer_To_be_inserted, &the_timer->Object.Node );
338  _Watchdog_Remove( &_Timer_Server->Timer );
339  _Thread_Delay_ended( _Timer_Server->Object.id, NULL );
340}
341
342/**
343 *  @brief rtems_timer_initiate_server
344 *
345 *  This directive creates and starts the server for task-based timers.
346 *  It must be invoked before any task-based timers can be initiated.
347 *
348 *  @param[in] priority is the timer server priority
349 *  @param[in] stack_size is the stack size in bytes
350 *  @param[in] attribute_set is the timer server attributes
351 *
352 *  @return This method returns RTEMS_SUCCESSFUL if successful and an
353 *          error code otherwise.
354 */
355rtems_status_code rtems_timer_initiate_server(
356  uint32_t             priority,
357  uint32_t             stack_size,
358  rtems_attribute      attribute_set
359)
360{
361  rtems_id            id;
362  rtems_status_code   status;
363  rtems_task_priority _priority;
364  static bool         initialized = false;
365  bool                tmpInitialized;
366
367  /*
368   *  Make sure the requested priority is valid.  The if is
369   *  structured so we check it is invalid before looking for
370   *  a specific invalid value as the default.
371   */
372  _priority = priority;
373  if ( !_RTEMS_tasks_Priority_is_valid( priority ) ) {
374    if ( priority != RTEMS_TIMER_SERVER_DEFAULT_PRIORITY )
375      return RTEMS_INVALID_PRIORITY;
376    _priority = 0;
377  }
378
379  /*
380   *  Just to make sure this is only called once.
381   */
382  _Thread_Disable_dispatch();
383    tmpInitialized  = initialized;
384    initialized = true;
385  _Thread_Enable_dispatch();
386
387  if ( tmpInitialized )
388    return RTEMS_INCORRECT_STATE;
389
390  /*
391   *  Initialize the set of timers to be inserted by the server.
392   */
393  _Chain_Initialize_empty( &_Timer_To_be_inserted );
394
395  /*
396   *  Create the Timer Server with the name the name of "TIME".  The attribute
397   *  RTEMS_SYSTEM_TASK allows us to set a priority to 0 which will makes it
398   *  higher than any other task in the system.  It can be viewed as a low
399   *  priority interrupt.  It is also always NO_PREEMPT so it looks like
400   *  an interrupt to other tasks.
401   *
402   *  We allow the user to override the default priority because the Timer
403   *  Server can invoke TSRs which must adhere to language run-time or
404   *  other library rules.  For example, if using a TSR written in Ada the
405   *  Server should run at the same priority as the priority Ada task.
406   *  Otherwise, the priority ceiling for the mutex used to protect the
407   *  GNAT run-time is violated.
408   */
409  status = rtems_task_create(
410    _Objects_Build_name('T','I','M','E'),           /* "TIME" */
411    _priority,            /* create with priority 1 since 0 is illegal */
412    stack_size,           /* let user specify stack size */
413    RTEMS_NO_PREEMPT,     /* no preempt is like an interrupt */
414                          /* user may want floating point but we need */
415                          /*   system task specified for 0 priority */
416    attribute_set | RTEMS_SYSTEM_TASK,
417    &id                   /* get the id back */
418  );
419  if (status) {
420    initialized = false;
421    return status;
422  }
423
424  /*
425   *  Do all the data structure initialization before starting the
426   *  Timer Server so we do not have to have a critical section.
427   */
428
429  /*
430   *  We work with the TCB pointer, not the ID, so we need to convert
431   *  to a TCB pointer from here out.
432   *
433   *  NOTE: Setting the pointer to the Timer Server TCB to a value other than
434   *        NULL indicates that task-based timer support is initialized.
435   */
436  _Timer_Server = (Thread_Control *)_Objects_Get_local_object(
437    &_RTEMS_tasks_Information,
438    _Objects_Get_index(id)
439  );
440
441  /*
442   *  Initialize the timer lists that the server will manage.
443   */
444  _Chain_Initialize_empty( &_Timer_Ticks_chain );
445  _Chain_Initialize_empty( &_Timer_Seconds_chain );
446
447  /*
448   *  Initialize the timers that will be used to control when the
449   *  Timer Server wakes up and services the task-based timers.
450   */
451  _Watchdog_Initialize( &_Timer_Server->Timer, _Thread_Delay_ended, id, NULL );
452  _Watchdog_Initialize( &_Timer_Seconds_timer, _Thread_Delay_ended, id, NULL );
453
454  /*
455   *  Initialize the pointer to the timer reset method so applications
456   *  that do not use the Timer Server do not have to pull it in.
457   */
458  _Timer_Server_schedule_operation = _Timer_Server_schedule_operation_method;
459
460  /*
461   *  Start the timer server
462   */
463  status = rtems_task_start(
464    id,                                    /* the id from create */
465    (rtems_task_entry) _Timer_Server_body, /* the timer server entry point */
466    0                                      /* there is no argument */
467  );
468 
469  #if defined(RTEMS_DEBUG)
470    /*
471     *  One would expect a call to rtems_task_delete() here to clean up
472     *  but there is actually no way (in normal circumstances) that the
473     *  start can fail.  The id and starting address are known to be
474     *  be good.  If this service fails, something is weirdly wrong on the
475     *  target such as a stray write in an ISR or incorrect memory layout.
476     */
477    if (status) {
478      initialized = false;
479    }
480  #endif
481
482  return status;
483}
Note: See TracBrowser for help on using the repository browser.