source: rtems/cpukit/rtems/src/timerserver.c @ 6e51c4c

4.104.115
Last change on this file since 6e51c4c was 6e51c4c, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 11/30/09 at 09:08:35

Added timer server control block
Removed _Timer_Server thread pointer
Added _Timer_server pointer to the default timer server control block
Rework of the timer server implementation.

  • Property mode set to 100644
File size: 16.8 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 *  Copyright (c) 2009 embedded brains GmbH.
19 *
20 *  The license and distribution terms for this file may be
21 *  found in the file LICENSE in this distribution or at
22 *  http://www.rtems.com/license/LICENSE.
23 *
24 *  $Id$
25 */
26
27#if HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include <rtems/system.h>
32#include <rtems/rtems/status.h>
33#include <rtems/rtems/support.h>
34#include <rtems/score/object.h>
35#include <rtems/score/thread.h>
36#include <rtems/rtems/timer.h>
37#include <rtems/score/tod.h>
38#include <rtems/score/watchdog.h>
39
40#include <rtems/rtems/tasks.h>
41#include <rtems/rtems/support.h>
42#include <rtems/score/thread.h>
43
44static Timer_server_Control _Timer_server_Default;
45
46static void _Timer_server_Stop_interval_system_watchdog(
47  Timer_server_Control *ts
48)
49{
50  _Watchdog_Remove( &ts->Interval_watchdogs.System_watchdog );
51}
52
53static void _Timer_server_Reset_interval_system_watchdog(
54  Timer_server_Control *ts
55)
56{
57  ISR_Level level;
58
59  _Timer_server_Stop_interval_system_watchdog( ts );
60
61  _ISR_Disable( level );
62  if ( !_Chain_Is_empty( &ts->Interval_watchdogs.Chain ) ) {
63    Watchdog_Interval delta_interval =
64      _Watchdog_First( &ts->Interval_watchdogs.Chain )->delta_interval;
65    _ISR_Enable( level );
66
67    /*
68     *  The unit is TICKS here.
69     */
70    _Watchdog_Insert_ticks(
71      &ts->Interval_watchdogs.System_watchdog,
72      delta_interval
73    );
74  } else {
75    _ISR_Enable( level );
76  }
77}
78
79static void _Timer_server_Stop_tod_system_watchdog(
80  Timer_server_Control *ts
81)
82{
83  _Watchdog_Remove( &ts->TOD_watchdogs.System_watchdog );
84}
85
86static void _Timer_server_Reset_tod_system_watchdog(
87  Timer_server_Control *ts
88)
89{
90  ISR_Level level;
91
92  _Timer_server_Stop_tod_system_watchdog( ts );
93
94  _ISR_Disable( level );
95  if ( !_Chain_Is_empty( &ts->TOD_watchdogs.Chain ) ) {
96    Watchdog_Interval delta_interval =
97      _Watchdog_First( &ts->TOD_watchdogs.Chain )->delta_interval;
98    _ISR_Enable( level );
99
100    /*
101     *  The unit is SECONDS here.
102     */
103    _Watchdog_Insert_seconds(
104      &ts->TOD_watchdogs.System_watchdog,
105      delta_interval
106    );
107  } else {
108    _ISR_Enable( level );
109  }
110}
111
112static void _Timer_server_Insert_timer(
113  Timer_server_Control *ts,
114  Timer_Control *timer
115)
116{
117  if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
118    _Watchdog_Insert( &ts->Interval_watchdogs.Chain, &timer->Ticker );
119  } else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
120    _Watchdog_Insert( &ts->TOD_watchdogs.Chain, &timer->Ticker );
121  }
122}
123
124static void _Timer_server_Insert_timer_and_make_snapshot(
125  Timer_server_Control *ts,
126  Timer_Control *timer
127)
128{
129  Watchdog_Control *first_watchdog;
130  Watchdog_Interval delta_interval;
131  Watchdog_Interval last_snapshot;
132  Watchdog_Interval snapshot;
133  Watchdog_Interval delta;
134  ISR_Level level;
135
136  /*
137   *  We have to update the time snapshots here, because otherwise we may have
138   *  problems with the integer range of the delta values.  The time delta DT
139   *  from the last snapshot to now may be arbitrarily long.  The last snapshot
140   *  is the reference point for the delta chain.  Thus if we do not update the
141   *  reference point we have to add DT to the initial delta of the watchdog
142   *  being inserted.  This could result in an integer overflow.
143   */
144
145  _Thread_Disable_dispatch();
146
147  if ( timer->the_class == TIMER_INTERVAL_ON_TASK ) {
148    /*
149     *  We have to advance the last known ticks value of the server and update
150     *  the watchdog chain accordingly.
151     */
152    _ISR_Disable( level );
153    snapshot = _Watchdog_Ticks_since_boot;
154    last_snapshot = ts->Interval_watchdogs.last_snapshot;
155    if ( !_Chain_Is_empty( &ts->Interval_watchdogs.Chain ) ) {
156      first_watchdog = _Watchdog_First( &ts->Interval_watchdogs.Chain );
157
158      /*
159       *  We assume adequate unsigned arithmetic here.
160       */
161      delta = snapshot - last_snapshot;
162
163      delta_interval = first_watchdog->delta_interval;
164      if (delta_interval > delta) {
165        delta_interval -= delta;
166      } else {
167        delta_interval = 0;
168      }
169      first_watchdog->delta_interval = delta_interval;
170    }
171    ts->Interval_watchdogs.last_snapshot = snapshot;
172    _ISR_Enable( level );
173
174    _Watchdog_Insert( &ts->Interval_watchdogs.Chain, &timer->Ticker );
175
176    if ( !ts->active ) {
177      _Timer_server_Reset_interval_system_watchdog( ts );
178    }
179  } else if ( timer->the_class == TIMER_TIME_OF_DAY_ON_TASK ) {
180    /*
181     *  We have to advance the last known seconds value of the server and update
182     *  the watchdog chain accordingly.
183     */
184    _ISR_Disable( level );
185    snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
186    last_snapshot = ts->TOD_watchdogs.last_snapshot;
187    if ( !_Chain_Is_empty( &ts->TOD_watchdogs.Chain ) ) {
188      first_watchdog = _Watchdog_First( &ts->TOD_watchdogs.Chain );
189      delta_interval = first_watchdog->delta_interval;
190      if ( snapshot > last_snapshot ) {
191        /*
192         *  We advanced in time.
193         */
194        delta = snapshot - last_snapshot;
195        if (delta_interval > delta) {
196          delta_interval -= delta;
197        } else {
198          delta_interval = 0;
199        }
200      } else {
201        /*
202         *  Someone put us in the past.
203         */
204        delta = last_snapshot - snapshot;
205        delta_interval += delta;
206      }
207      first_watchdog->delta_interval = delta_interval;
208    }
209    ts->TOD_watchdogs.last_snapshot = snapshot;
210    _ISR_Enable( level );
211
212    _Watchdog_Insert( &ts->TOD_watchdogs.Chain, &timer->Ticker );
213
214    if ( !ts->active ) {
215      _Timer_server_Reset_tod_system_watchdog( ts );
216    }
217  }
218
219  _Thread_Enable_dispatch();
220}
221
222static void _Timer_server_Schedule_operation_method(
223  Timer_server_Control *ts,
224  Timer_Control *timer
225)
226{
227  if ( ts->insert_chain == NULL ) {
228    _Timer_server_Insert_timer_and_make_snapshot( ts, timer );
229  } else {
230    /*
231     *  We interrupted a critical section of the timer server.  The timer
232     *  server is not preemptible, so we must be in interrupt context here.  No
233     *  thread dispatch will happen until the timer server finishes its
234     *  critical section.  We have to use the protected chain methods because
235     *  we may be interrupted by a higher priority interrupt.
236     */
237    _Chain_Append( ts->insert_chain, &timer->Object.Node );
238  }
239}
240
241static void _Timer_server_Process_interval_watchdogs(
242  Timer_server_Watchdogs *watchdogs,
243  Chain_Control *fire_chain
244)
245{
246  Watchdog_Interval snapshot = _Watchdog_Ticks_since_boot;
247
248  /*
249   *  We assume adequate unsigned arithmetic here.
250   */
251  Watchdog_Interval delta = snapshot - watchdogs->last_snapshot;
252
253  watchdogs->last_snapshot = snapshot;
254
255  _Watchdog_Adjust_to_chain( &watchdogs->Chain, delta, fire_chain );
256}
257
258static void _Timer_server_Process_tod_watchdogs(
259  Timer_server_Watchdogs *watchdogs,
260  Chain_Control *fire_chain
261)
262{
263  Watchdog_Interval snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
264  Watchdog_Interval last_snapshot = watchdogs->last_snapshot;
265  Watchdog_Interval delta;
266
267  /*
268   *  Process the seconds chain.  Start by checking that the Time
269   *  of Day (TOD) has not been set backwards.  If it has then
270   *  we want to adjust the watchdogs->Chain to indicate this.
271   */
272  if ( snapshot > last_snapshot ) {
273    /*
274     *  This path is for normal forward movement and cases where the
275     *  TOD has been set forward.
276     */
277    delta = snapshot - last_snapshot;
278    _Watchdog_Adjust_to_chain( &watchdogs->Chain, delta, fire_chain );
279
280  } else if ( snapshot < last_snapshot ) {
281     /*
282      *  The current TOD is before the last TOD which indicates that
283      *  TOD has been set backwards.
284      */
285     delta = last_snapshot - snapshot;
286     _Watchdog_Adjust( &watchdogs->Chain, WATCHDOG_BACKWARD, delta );
287  }
288
289  watchdogs->last_snapshot = snapshot;
290}
291
292static void _Timer_server_Process_insertions( Timer_server_Control *ts )
293{
294  while ( true ) {
295    Timer_Control *timer = (Timer_Control *) _Chain_Get( ts->insert_chain );
296
297    if ( timer == NULL ) {
298      break;
299    }
300
301    _Timer_server_Insert_timer( ts, timer );
302  }
303}
304
305static void _Timer_server_Get_watchdogs_that_fire_now(
306  Timer_server_Control *ts,
307  Chain_Control *insert_chain,
308  Chain_Control *fire_chain
309)
310{
311  /*
312   *  Afterwards all timer inserts are directed to this chain and the interval
313   *  and TOD chains will be no more modified by other parties.
314   */
315  ts->insert_chain = insert_chain;
316
317  while ( true ) {
318    ISR_Level level;
319
320    /*
321     *  Remove all the watchdogs that need to fire so we can invoke them.
322     */
323    _Timer_server_Process_interval_watchdogs(
324      &ts->Interval_watchdogs,
325      fire_chain
326    );
327    _Timer_server_Process_tod_watchdogs( &ts->TOD_watchdogs, fire_chain );
328
329    /*
330     *  The insertions have to take place here, because they reference the
331     *  current time.  The previous process methods take a snapshot of the
332     *  current time.  In case someone inserts a watchdog with an initial value
333     *  of zero it will be processed in the next iteration of the timer server
334     *  body loop.
335     */
336    _Timer_server_Process_insertions( ts );
337
338    _ISR_Disable( level );
339    if ( _Chain_Is_empty( insert_chain ) ) {
340      ts->insert_chain = NULL;
341      _ISR_Enable( level );
342
343      break;
344    } else {
345      _ISR_Enable( level );
346    }
347  }
348}
349
350/**
351 *  @brief Timer server body.
352 *
353 *  This is the server for task based timers.  This task executes whenever a
354 *  task-based timer should fire.  It services both "after" and "when" timers.
355 *  It is not created automatically but must be created explicitly by the
356 *  application before task-based timers may be initiated.  The parameter
357 *  @a arg points to the corresponding timer server control block.
358 */
359static rtems_task _Timer_server_Body(
360  rtems_task_argument arg
361)
362{
363  Timer_server_Control *ts = (Timer_server_Control *) arg;
364  Chain_Control insert_chain;
365  Chain_Control fire_chain;
366
367  _Chain_Initialize_empty( &insert_chain );
368  _Chain_Initialize_empty( &fire_chain );
369
370  while ( true ) {
371    _Timer_server_Get_watchdogs_that_fire_now( ts, &insert_chain, &fire_chain );
372
373    if ( !_Chain_Is_empty( &fire_chain ) ) {
374      /*
375       *  Fire the watchdogs.
376       */
377      while ( true ) {
378        Watchdog_Control *watchdog;
379        ISR_Level level;
380
381        /*
382         *  It is essential that interrupts are disable here since an interrupt
383         *  service routine may remove a watchdog from the chain.
384         */
385        _ISR_Disable( level );
386        watchdog = (Watchdog_Control *) _Chain_Get_unprotected( &fire_chain );
387        if ( watchdog != NULL ) {
388          watchdog->state = WATCHDOG_INACTIVE;
389          _ISR_Enable( level );
390        } else {
391          _ISR_Enable( level );
392
393          break;
394        }
395
396        /*
397         *  The timer server may block here and wait for resources or time.
398         *  The system watchdogs are inactive and will remain inactive since
399         *  the active flag of the timer server is true.
400         */
401        (*watchdog->routine)( watchdog->id, watchdog->user_data );
402      }
403    } else {
404      ts->active = false;
405
406      /*
407       *  Block until there is something to do.
408       */
409      _Thread_Disable_dispatch();
410        _Thread_Set_state( ts->thread, STATES_DELAYING );
411        _Timer_server_Reset_interval_system_watchdog( ts );
412        _Timer_server_Reset_tod_system_watchdog( ts );
413      _Thread_Enable_dispatch();
414
415      ts->active = true;
416
417      /*
418       *  Maybe an interrupt did reset the system timers, so we have to stop
419       *  them here.  Since we are active now, there will be no more resets
420       *  until we are inactive again.
421       */
422      _Timer_server_Stop_interval_system_watchdog( ts );
423      _Timer_server_Stop_tod_system_watchdog( ts );
424    }
425  }
426}
427
428/**
429 *  @brief rtems_timer_initiate_server
430 *
431 *  This directive creates and starts the server for task-based timers.
432 *  It must be invoked before any task-based timers can be initiated.
433 *
434 *  @param[in] priority is the timer server priority
435 *  @param[in] stack_size is the stack size in bytes
436 *  @param[in] attribute_set is the timer server attributes
437 *
438 *  @return This method returns RTEMS_SUCCESSFUL if successful and an
439 *          error code otherwise.
440 */
441rtems_status_code rtems_timer_initiate_server(
442  uint32_t             priority,
443  uint32_t             stack_size,
444  rtems_attribute      attribute_set
445)
446{
447  rtems_id              id;
448  rtems_status_code     status;
449  rtems_task_priority   _priority;
450  static bool           initialized = false;
451  bool                  tmpInitialized;
452  Timer_server_Control *ts = &_Timer_server_Default;
453
454  /*
455   *  Make sure the requested priority is valid.  The if is
456   *  structured so we check it is invalid before looking for
457   *  a specific invalid value as the default.
458   */
459  _priority = priority;
460  if ( !_RTEMS_tasks_Priority_is_valid( priority ) ) {
461    if ( priority != RTEMS_TIMER_SERVER_DEFAULT_PRIORITY )
462      return RTEMS_INVALID_PRIORITY;
463    _priority = 0;
464  }
465
466  /*
467   *  Just to make sure this is only called once.
468   */
469  _Thread_Disable_dispatch();
470    tmpInitialized  = initialized;
471    initialized = true;
472  _Thread_Enable_dispatch();
473
474  if ( tmpInitialized )
475    return RTEMS_INCORRECT_STATE;
476
477  /*
478   *  Create the Timer Server with the name the name of "TIME".  The attribute
479   *  RTEMS_SYSTEM_TASK allows us to set a priority to 0 which will makes it
480   *  higher than any other task in the system.  It can be viewed as a low
481   *  priority interrupt.  It is also always NO_PREEMPT so it looks like
482   *  an interrupt to other tasks.
483   *
484   *  We allow the user to override the default priority because the Timer
485   *  Server can invoke TSRs which must adhere to language run-time or
486   *  other library rules.  For example, if using a TSR written in Ada the
487   *  Server should run at the same priority as the priority Ada task.
488   *  Otherwise, the priority ceiling for the mutex used to protect the
489   *  GNAT run-time is violated.
490   */
491  status = rtems_task_create(
492    _Objects_Build_name('T','I','M','E'),           /* "TIME" */
493    _priority,            /* create with priority 1 since 0 is illegal */
494    stack_size,           /* let user specify stack size */
495    RTEMS_NO_PREEMPT,     /* no preempt is like an interrupt */
496                          /* user may want floating point but we need */
497                          /*   system task specified for 0 priority */
498    attribute_set | RTEMS_SYSTEM_TASK,
499    &id                   /* get the id back */
500  );
501  if (status) {
502    initialized = false;
503    return status;
504  }
505
506  /*
507   *  Do all the data structure initialization before starting the
508   *  Timer Server so we do not have to have a critical section.
509   */
510
511  /*
512   *  We work with the TCB pointer, not the ID, so we need to convert
513   *  to a TCB pointer from here out.
514   */
515  ts->thread = (Thread_Control *)_Objects_Get_local_object(
516    &_RTEMS_tasks_Information,
517    _Objects_Get_index(id)
518  );
519
520  /*
521   *  Initialize the timer lists that the server will manage.
522   */
523  _Chain_Initialize_empty( &ts->Interval_watchdogs.Chain );
524  _Chain_Initialize_empty( &ts->TOD_watchdogs.Chain );
525
526  /*
527   *  Initialize the timers that will be used to control when the
528   *  Timer Server wakes up and services the task-based timers.
529   */
530  _Watchdog_Initialize(
531    &ts->Interval_watchdogs.System_watchdog,
532    _Thread_Delay_ended,
533    id,
534    NULL
535  );
536  _Watchdog_Initialize(
537    &ts->TOD_watchdogs.System_watchdog,
538    _Thread_Delay_ended,
539    id,
540    NULL
541  );
542
543  /*
544   *  Initialize the pointer to the timer schedule method so applications that
545   *  do not use the Timer Server do not have to pull it in.
546   */
547  ts->schedule_operation = _Timer_server_Schedule_operation_method;
548
549  ts->Interval_watchdogs.last_snapshot = _Watchdog_Ticks_since_boot;
550  ts->TOD_watchdogs.last_snapshot = (Watchdog_Interval) _TOD_Seconds_since_epoch();
551
552  ts->insert_chain = NULL;
553  ts->active = false;
554
555  /*
556   * The default timer server is now available.
557   */
558  _Timer_server = ts;
559
560  /*
561   *  Start the timer server
562   */
563  status = rtems_task_start(
564    id,
565    _Timer_server_Body,
566    (rtems_task_argument) ts
567  );
568
569  #if defined(RTEMS_DEBUG)
570    /*
571     *  One would expect a call to rtems_task_delete() here to clean up
572     *  but there is actually no way (in normal circumstances) that the
573     *  start can fail.  The id and starting address are known to be
574     *  be good.  If this service fails, something is weirdly wrong on the
575     *  target such as a stray write in an ISR or incorrect memory layout.
576     */
577    if (status) {
578      initialized = false;
579    }
580  #endif
581
582  return status;
583}
Note: See TracBrowser for help on using the repository browser.