source: rtems/cpukit/rtems/src/timerserver.c @ f6b7b7ba

4.115
Last change on this file since f6b7b7ba was f6b7b7ba, checked in by Sebastian Huber <sebastian.huber@…>, on 06/18/14 at 10:11:04

score: Fix _Thread_Delay_ended() on SMP

Suppose we have two tasks A and B and two processors. Task A is about
to delete task B. Now task B calls rtems_task_wake_after(1) on the
other processor. Task B will block on the Giant lock. Task A
progresses with the task B deletion until it has to wait for
termination. Now task B obtains the Giant lock, sets its state to
STATES_DELAYING, initializes its watchdog timer and waits. Eventually
_Thread_Delay_ended() is called, but now _Thread_Get() returned NULL
since the thread is already marked as deleted. Thus task B remained
forever in the STATES_DELAYING state.

Instead of passing the thread identifier use the thread control block
directly via the watchdog user argument. This makes
_Thread_Delay_ended() also a bit more efficient.

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