TNeo  v1.08
tn_timer.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeo: real-time kernel initially based on TNKernel
4  *
5  * TNKernel: copyright 2004, 2013 Yuri Tiomkin.
6  * PIC32-specific routines: copyright 2013, 2014 Anders Montonen.
7  * TNeo: copyright 2014 Dmitry Frank.
8  *
9  * TNeo was born as a thorough review and re-implementation of
10  * TNKernel. The new kernel has well-formed code, inherited bugs are fixed
11  * as well as new features being added, and it is tested carefully with
12  * unit-tests.
13  *
14  * API is changed somewhat, so it's not 100% compatible with TNKernel,
15  * hence the new name: TNeo.
16  *
17  * Permission to use, copy, modify, and distribute this software in source
18  * and binary forms and its documentation for any purpose and without fee
19  * is hereby granted, provided that the above copyright notice appear
20  * in all copies and that both that copyright notice and this permission
21  * notice appear in supporting documentation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY FRANK OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 /**
38  * \file
39  *
40  * Timer is a kernel object that is used to ask the kernel to call some
41  * user-provided function at a particular time in the future, based on the
42  * $(TN_SYS_TIMER_LINK) tick.
43  *
44  * If you need to repeatedly wake up particular task, you can create semaphore
45  * which you should \ref tn_sem_wait() "wait for" in the task, and \ref
46  * tn_sem_isignal() "signal" in the timer callback (remember that you should
47  * use `tn_sem_isignal()` in this callback, since it is called from an ISR).
48  *
49  * If you need to perform rather fast action (such as toggle some pin, or the
50  * like), consider doing that right in the timer callback, in order to avoid
51  * context switch overhead.
52  *
53  * The timer callback approach provides ultimate flexibility.
54  *
55  * In the spirit of TNeo, timers are as lightweight as possible. That's
56  * why there is only one type of timer: the single-shot timer. If you need your
57  * timer to fire repeatedly, you can easily restart it from the timer function
58  * by the `tn_timer_start()`, so it's not a problem.
59  *
60  * When timer fires, the user-provided function is called. Be aware of the
61  * following:
62  *
63  * - Function is called from an ISR context (namely, from $(TN_SYS_TIMER_LINK)
64  * ISR, by the `tn_tick_int_processing()`);
65  * - Function is called with global interrupts enabled.
66  *
67  * Consequently:
68  *
69  * - It's legal to call interrupt services from this function;
70  * - You should make sure that your interrupt stack is enough for this
71  * function;
72  * - The function should be as fast as possible;
73  *
74  * See `#TN_TimerFunc` for the prototype of the function that could be
75  * scheduled.
76  *
77  * TNeo offers two implementations of timers: static and dynamic. Refer
78  * to the page \ref time_ticks for details.
79  *
80  * \section timers_static_implementation Implementation of static timers
81  *
82  * Although you don't have to understand the implementation of timers to use
83  * them, it is probably worth knowing, particularly because the kernel have an
84  * option `#TN_TICK_LISTS_CNT` to customize the balance between performance of
85  * `tn_tick_int_processing()` and memory occupied by timers.
86  *
87  * The easiest implementation of timers could be something like this: we
88  * have just a single list with all active timers, and at every system tick
89  * we should walk through all the timers in this list, and do the following
90  * with each timer:
91  *
92  * - Decrement timeout by 1
93  * - If new timeout is 0, then remove that timer from the list (i.e. make timer
94  * inactive), and fire the appropriate timer function.
95  *
96  * This approach has drawbacks:
97  *
98  * - We can't manage timers from the function called by timer. If we do so
99  * (say, if we start new timer), then the timer list gets modified. But we
100  * are currently iterating through this list, so, it's quite easy to mix
101  * things up.
102  * - It is inefficient on rather large amount of timers and/or with large
103  * timeouts: we should iterate through all of them each system tick.
104  *
105  * The latter is probably not so critical in the embedded world since large
106  * amount of timers is unlikely there; whereas the former is actually notable.
107  *
108  * So, different approach was applied. The main idea is taken from the mainline
109  * Linux kernel source, but the implementation was simplified much because (1)
110  * embedded systems have much less resources, and (2) the kernel doesn't need
111  * to scale as well as Linux does. You can read about Linux timers
112  * implementation in the book "Linux Device Drivers", 3rd edition:
113  *
114  * - Time, Delays, and Deferred Work
115  * - Kernel Timers
116  * - The Implementation of Kernel Timers
117  *
118  * This book is freely available at http://lwn.net/Kernel/LDD3/ .
119  *
120  * So, TNeo's implementation:
121  *
122  * We have configurable value `N` that is a power of two, typical values are
123  * `4`, `8` or `16`.
124  *
125  * If the timer expires in the next `1` to `(N - 1)` system ticks, it is added
126  * to one of the `N` lists (the so-called "tick" lists) devoted to short-range
127  * timers using the least significant bits of the `timeout` value. If it
128  * expires farther in the future, it is added to the "generic" list.
129  *
130  * Each `N`-th system tick, all the timers from "generic" list are walked
131  * through, and the following is performed with each timer:
132  *
133  * - `timeout` value decremented by `N`
134  * - if resulting `timeout` is less than `N`, timer is moved to the appropriate
135  * "tick" list.
136  *
137  * At *every* system tick, all the timers from current "tick" list are fired
138  * unconditionally. This is an efficient and nice solution.
139  *
140  * The attentive reader may want to ask why do we use `(N - 1)` "tick" lists if
141  * we actually have `N` lists. That's because, again, we want to be able to
142  * modify timers from the timer function. If we use `N` lists, and user wants
143  * to add new timer with `timeout` equal to `N`, then new timer will be added
144  * to the same list which is iterated through at the moment, and things will be
145  * mixed up.
146  *
147  * If we use `(N - 1)` lists, we are guaranteed that new timers can't be added
148  * to the current "tick" list while we are iterating through it.
149  * (although timer can be deleted from that list, but it's ok)
150  *
151  * The `N` in the TNeo is configured by the compile-time option
152  * `#TN_TICK_LISTS_CNT`.
153  */
154 
155 
156 #ifndef _TN_TIMER_H
157 #define _TN_TIMER_H
158 
159 /*******************************************************************************
160  * INCLUDED FILES
161  ******************************************************************************/
162 
163 #include "tn_list.h"
164 #include "tn_common.h"
165 
166 
167 
168 #ifdef __cplusplus
169 extern "C" { /*}*/
170 #endif
171 
172 /*******************************************************************************
173  * PUBLIC TYPES
174  ******************************************************************************/
175 
176 struct TN_Timer;
177 
178 
179 /**
180  * Prototype of the function that should be called by timer.
181  *
182  * When timer fires, the user-provided function is called. Be aware of the
183  * following:
184  * - Function is called from ISR context (namely, from $(TN_SYS_TIMER_LINK)
185  * ISR, by the `tn_tick_int_processing()`);
186  * - Function is called with global interrupts enabled.
187  *
188  * Consequently:
189  *
190  * - It's legal to call interrupt services from this function;
191  * - The function should be as fast as possible.
192  *
193  * @param timer
194  * Timer that caused function to be called
195  * @param p_user_data
196  * The user-provided pointer given to `tn_timer_create()`.
197  */
198 typedef void (TN_TimerFunc)(struct TN_Timer *timer, void *p_user_data);
199 
200 /**
201  * Timer
202  */
203 struct TN_Timer {
204  ///
205  /// id for object validity verification.
206  /// This field is in the beginning of the structure to make it easier
207  /// to detect memory corruption.
209  ///
210  /// A list item to be included in the $(TN_SYS_TIMER_LINK) queue
212  ///
213  /// Function to be called by timer
215  ///
216  /// User data pointer that is given to user-provided `func`.
217  void *p_user_data;
218 
219 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
220  ///
221  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
222  ///
223  /// Tick count value when timer was started
225  ///
226  /// $(TN_IF_ONLY_DYNAMIC_TICK_SET)
227  ///
228  /// Timeout value (it is set just once, and stays unchanged until timer is
229  /// expired, cancelled or restarted)
231 #endif
232 
233 #if !TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
234  ///
235  /// $(TN_IF_ONLY_DYNAMIC_TICK_NOT_SET)
236  ///
237  /// Current (left) timeout value
239 #endif
240 };
241 
242 
243 
244 
245 
246 #if TN_DYNAMIC_TICK || defined(DOXYGEN_ACTIVE)
247 
248 /**
249  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
250  *
251  * Prototype of callback function that should schedule next time to call
252  * `tn_tick_int_processing()`.
253  *
254  * See `tn_callback_dyn_tick_set()`
255  *
256  * @param timeout
257  * Timeout after which `tn_tick_int_processing()` should be called next
258  * time. Note the following:
259  * - It might be `#TN_WAIT_INFINITE`, which means that there are no active
260  * timeouts, and so, there's no need for tick interrupt at all.
261  * - It might be `0`; in this case, it's <i>already</i> time to call
262  * `tn_tick_int_processing()`. You might want to set interrupt request
263  * bit then, in order to get to it as soon as possible.
264  * - In other cases, the function should schedule next call to
265  * `tn_tick_int_processing()` in the `timeout` tick periods.
266  *
267  */
269 
270 /**
271  * $(TN_IF_ONLY_DYNAMIC_TICK_SET)
272  *
273  * Prototype of callback function that should return current system tick
274  * counter value.
275  *
276  * See `tn_callback_dyn_tick_set()`
277  *
278  * @return current system tick counter value.
279  */
280 typedef TN_TickCnt (TN_CBTickCntGet)(void);
281 
282 #endif
283 
284 
285 
286 
287 /*******************************************************************************
288  * PROTECTED GLOBAL DATA
289  ******************************************************************************/
290 
291 /*******************************************************************************
292  * DEFINITIONS
293  ******************************************************************************/
294 
295 /*******************************************************************************
296  * PUBLIC FUNCTION PROTOTYPES
297  ******************************************************************************/
298 
299 /**
300  * Construct the timer. `id_timer` field should not contain
301  * `#TN_ID_TIMER`, otherwise, `#TN_RC_WPARAM` is returned.
302  *
303  * $(TN_CALL_FROM_TASK)
304  * $(TN_CALL_FROM_ISR)
305  * $(TN_LEGEND_LINK)
306  *
307  * @param timer
308  * Pointer to already allocated `struct TN_Timer`
309  * @param func
310  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
311  * @param p_user_data
312  * User data pointer that is given to user-provided `func`.
313  *
314  * @return
315  * * `#TN_RC_OK` if timer was successfully created;
316  * * `#TN_RC_WPARAM` if wrong params were given.
317  */
319  struct TN_Timer *timer,
321  void *p_user_data
322  );
323 
324 /**
325  * Destruct the timer. If the timer is active, it is cancelled first.
326  *
327  * $(TN_CALL_FROM_TASK)
328  * $(TN_CALL_FROM_ISR)
329  * $(TN_LEGEND_LINK)
330  *
331  * @param timer timer to destruct
332  *
333  * @return
334  * * `#TN_RC_OK` if timer was successfully deleted;
335  * * `#TN_RC_WCONTEXT` if called from wrong context;
336  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
337  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
338  */
339 enum TN_RCode tn_timer_delete(struct TN_Timer *timer);
340 
341 /**
342  * Start or restart the timer: that is, schedule the timer's function (given to
343  * `tn_timer_create()`) to be called later by the kernel. See `TN_TimerFunc()`.
344  *
345  * It is legal to restart already active timer. In this case, the timer will be
346  * cancelled first.
347  *
348  * $(TN_CALL_FROM_TASK)
349  * $(TN_CALL_FROM_ISR)
350  * $(TN_LEGEND_LINK)
351  *
352  * @param timer
353  * Timer to start
354  * @param timeout
355  * Number of system ticks after which timer should fire (i.e. function
356  * should be called). **Note** that `timeout` can't be `#TN_WAIT_INFINITE` or
357  * `0`.
358  *
359  * @return
360  * * `#TN_RC_OK` if timer was successfully started;
361  * * `#TN_RC_WCONTEXT` if called from wrong context;
362  * * `#TN_RC_WPARAM` if wrong params were given: say, `timeout` is either
363  * `#TN_WAIT_INFINITE` or `0`.
364  * * If `#TN_CHECK_PARAM` is non-zero, additional return code
365  * is available: `#TN_RC_INVALID_OBJ`.
366  */
367 enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_TickCnt timeout);
368 
369 /**
370  * If timer is active, cancel it. If timer is already inactive, nothing is
371  * changed.
372  *
373  * $(TN_CALL_FROM_TASK)
374  * $(TN_CALL_FROM_ISR)
375  * $(TN_LEGEND_LINK)
376  *
377  * @param timer
378  * Timer to cancel
379  *
380  * @return
381  * * `#TN_RC_OK` if timer was successfully cancelled;
382  * * `#TN_RC_WCONTEXT` if called from wrong context;
383  * * If `#TN_CHECK_PARAM` is non-zero, additional return codes
384  * are available: `#TN_RC_WPARAM` and `#TN_RC_INVALID_OBJ`.
385  */
386 enum TN_RCode tn_timer_cancel(struct TN_Timer *timer);
387 
388 /**
389  * Set user-provided function and pointer to user data for the timer.
390  * Can be called if timer is either active or inactive.
391  *
392  * $(TN_CALL_FROM_TASK)
393  * $(TN_CALL_FROM_ISR)
394  * $(TN_LEGEND_LINK)
395  *
396  * @param timer
397  * Pointer to timer
398  * @param func
399  * Function to be called by timer, can't be `TN_NULL`. See `TN_TimerFunc()`
400  * @param p_user_data
401  * User data pointer that is given to user-provided `func`.
402  *
403  * @return
404  * * `#TN_RC_OK` if operation was successfull;
405  * * `#TN_RC_WPARAM` if wrong params were given.
406  */
408  struct TN_Timer *timer,
410  void *p_user_data
411  );
412 
413 /**
414  * Returns whether given timer is active or inactive.
415  *
416  * $(TN_CALL_FROM_TASK)
417  * $(TN_CALL_FROM_ISR)
418  * $(TN_LEGEND_LINK)
419  *
420  * @param timer
421  * Pointer to timer
422  * @param p_is_active
423  * Pointer to `#TN_BOOL` variable in which resulting value should be stored
424  *
425  * @return
426  * * `#TN_RC_OK` if operation was successfull;
427  * * `#TN_RC_WPARAM` if wrong params were given.
428  */
429 enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, TN_BOOL *p_is_active);
430 
431 /**
432  * Returns how many $(TN_SYS_TIMER_LINK) ticks (at most) is left for the timer
433  * to expire. If timer is inactive, 0 is returned.
434  *
435  * $(TN_CALL_FROM_TASK)
436  * $(TN_CALL_FROM_ISR)
437  * $(TN_LEGEND_LINK)
438  *
439  * @param timer
440  * Pointer to timer
441  * @param p_time_left
442  * Pointer to `#TN_TickCnt` variable in which resulting value should be
443  * stored
444  *
445  * @return
446  * * `#TN_RC_OK` if operation was successfull;
447  * * `#TN_RC_WPARAM` if wrong params were given.
448  */
450  struct TN_Timer *timer,
451  TN_TickCnt *p_time_left
452  );
453 
454 #ifdef __cplusplus
455 } /* extern "C" */
456 #endif
457 
458 #endif // _TN_TIMER_H
459 
460 /*******************************************************************************
461  * end of file
462  ******************************************************************************/
463 
464 
enum TN_RCode tn_timer_cancel(struct TN_Timer *timer)
If timer is active, cancel it.
enum TN_RCode tn_timer_create(struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data)
Construct the timer.
struct TN_ListItem timer_queue
A list item to be included in the system timer queue.
Definition: tn_timer.h:211
enum TN_RCode tn_timer_delete(struct TN_Timer *timer)
Destruct the timer.
Circular doubly linked list, for internal kernel usage.
void() TN_TimerFunc(struct TN_Timer *timer, void *p_user_data)
Prototype of the function that should be called by timer.
Definition: tn_timer.h:198
enum TN_RCode tn_timer_is_active(struct TN_Timer *timer, TN_BOOL *p_is_active)
Returns whether given timer is active or inactive.
TN_RCode
Result code returned by kernel services.
Definition: tn_common.h:81
TN_ObjId
Magic number for object validity verification.
Definition: tn_common.h:65
unsigned long TN_TickCnt
Type for system tick count, it is used by the kernel to represent absolute tick count value as well a...
Definition: tn_common.h:188
enum TN_RCode tn_timer_set_func(struct TN_Timer *timer, TN_TimerFunc *func, void *p_user_data)
Set user-provided function and pointer to user data for the timer.
Definitions used through the whole kernel.
TN_TickCnt start_tick_cnt
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:224
enum TN_ObjId id_timer
id for object validity verification.
Definition: tn_timer.h:208
void() TN_CBTickSchedule(TN_TickCnt timeout)
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:268
Timer.
Definition: tn_timer.h:203
TN_TickCnt() TN_CBTickCntGet(void)
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:280
TN_TimerFunc * func
Function to be called by timer.
Definition: tn_timer.h:214
TN_TickCnt timeout
Available if only TN_DYNAMIC_TICK is set.
Definition: tn_timer.h:230
TN_TickCnt timeout_cur
Available if only TN_DYNAMIC_TICK is not set.
Definition: tn_timer.h:238
enum TN_RCode tn_timer_start(struct TN_Timer *timer, TN_TickCnt timeout)
Start or restart the timer: that is, schedule the timer&#39;s function (given to tn_timer_create()) to be...
void * p_user_data
User data pointer that is given to user-provided func.
Definition: tn_timer.h:217
enum TN_RCode tn_timer_time_left(struct TN_Timer *timer, TN_TickCnt *p_time_left)
Returns how many system timer ticks (at most) is left for the timer to expire.
Circular doubly linked list item, for internal kernel usage.
Definition: tn_list.h:63
#define TN_BOOL
boolean type definition
Definition: tn_common.h:213