Embedded Template Library 1.0
message_timer_atomic.h
1/******************************************************************************
2The MIT License(MIT)
3
4Embedded Template Library.
5https://github.com/ETLCPP/etl
6https://www.etlcpp.com
7
8Copyright(c) 2021 John Wellbelove
9
10Permission is hereby granted, free of charge, to any person obtaining a copy
11of this software and associated documentation files(the "Software"), to deal
12in the Software without restriction, including without limitation the rights
13to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14copies of the Software, and to permit persons to whom the Software is
15furnished to do so, subject to the following conditions :
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26SOFTWARE.
27******************************************************************************/
28
29#ifndef ETL_MESSAGE_TIMER_ATOMIC_INCLUDED
30#define ETL_MESSAGE_TIMER_ATOMIC_INCLUDED
31
32#include "platform.h"
33#include "nullptr.h"
34#include "message_types.h"
35#include "message.h"
36#include "message_router.h"
37#include "message_bus.h"
38#include "static_assert.h"
39#include "timer.h"
40#include "atomic.h"
41#include "algorithm.h"
42
43#include <stdint.h>
44
45#if ETL_HAS_ATOMIC
46
47namespace etl
48{
49 //***************************************************************************
51 //***************************************************************************
52 template <typename TSemaphore>
53 class imessage_timer_atomic
54 {
55 public:
56
57 //*******************************************
59 //*******************************************
60 etl::timer::id::type register_timer(const etl::imessage& message_,
61 etl::imessage_router& router_,
62 uint32_t period_,
63 bool repeating_,
64 etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
65 {
66 etl::timer::id::type id = etl::timer::id::NO_TIMER;
67
68 bool is_space = (registered_timers < MAX_TIMERS);
69
70 if (is_space)
71 {
72 // There's no point adding null message routers.
73 if (!router_.is_null_router())
74 {
75 // Search for the free space.
76 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
77 {
78 timer_data& timer = timer_array[i];
79
80 if (timer.id == etl::timer::id::NO_TIMER)
81 {
82 // Create in-place.
83 new (&timer) timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
84 ++registered_timers;
85 id = i;
86 break;
87 }
88 }
89 }
90 }
91
92 return id;
93 }
94
95 //*******************************************
97 //*******************************************
98 bool unregister_timer(etl::timer::id::type id_)
99 {
100 bool result = false;
101
102 if (id_ != etl::timer::id::NO_TIMER)
103 {
104 timer_data& timer = timer_array[id_];
105
106 if (timer.id != etl::timer::id::NO_TIMER)
107 {
108 if (timer.is_active())
109 {
110 ++process_semaphore;
111 active_list.remove(timer.id, true);
112 --process_semaphore;
113 }
114
115 // Reset in-place.
116 new (&timer) timer_data();
117 --registered_timers;
118
119 result = true;
120 }
121 }
122
123 return result;
124 }
125
126 //*******************************************
128 //*******************************************
129 void enable(bool state_)
130 {
131 enabled = state_;
132 }
133
134 //*******************************************
136 //*******************************************
137 bool is_running() const
138 {
139 return enabled;
140 }
141
142 //*******************************************
144 //*******************************************
145 void clear()
146 {
147 ++process_semaphore;
148 active_list.clear();
149 --process_semaphore;
150
151 for (int i = 0; i < MAX_TIMERS; ++i)
152 {
153 new (&timer_array[i]) timer_data();
154 }
155
156 registered_timers = 0U;
157 }
158
159 //*******************************************
160 // Called by the timer service to indicate the
161 // amount of time that has elapsed since the last successful call to 'tick'.
162 // Returns true if the tick was processed,
163 // false if not.
164 //*******************************************
165 bool tick(uint32_t count)
166 {
167 if (enabled)
168 {
169 if (process_semaphore == 0U)
170 {
171 // We have something to do?
172 bool has_active = !active_list.empty();
173
174 if (has_active)
175 {
176 while (has_active && (count >= active_list.front().delta))
177 {
178 timer_data& timer = active_list.front();
179
180 count -= timer.delta;
181
182 active_list.remove(timer.id, true);
183
184 if (timer.p_router != ETL_NULLPTR)
185 {
186 timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
187 }
188
189 if (timer.repeating)
190 {
191 timer.delta = timer.period;
192 active_list.insert(timer.id);
193 }
194
195 has_active = !active_list.empty();
196 }
197
198 if (has_active)
199 {
200 // Subtract any remainder from the next due timeout.
201 active_list.front().delta -= count;
202 }
203 }
204
205 return true;
206 }
207 }
208
209 return false;
210 }
211
212 //*******************************************
214 //*******************************************
215 bool start(etl::timer::id::type id_, bool immediate_ = false)
216 {
217 bool result = false;
218
219 // Valid timer id?
220 if (id_ != etl::timer::id::NO_TIMER)
221 {
222 timer_data& timer = timer_array[id_];
223
224 // Registered timer?
225 if (timer.id != etl::timer::id::NO_TIMER)
226 {
227 // Has a valid period.
228 if (timer.period != etl::timer::state::INACTIVE)
229 {
230 ++process_semaphore;
231 if (timer.is_active())
232 {
233 active_list.remove(timer.id, false);
234 }
235
236 timer.delta = immediate_ ? 0U : timer.period;
237 active_list.insert(timer.id);
238 --process_semaphore;
239
240 result = true;
241 }
242 }
243 }
244
245 return result;
246 }
247
248 //*******************************************
250 //*******************************************
251 bool stop(etl::timer::id::type id_)
252 {
253 bool result = false;
254
255 // Valid timer id?
256 if (id_ != etl::timer::id::NO_TIMER)
257 {
258 timer_data& timer = timer_array[id_];
259
260 // Registered timer?
261 if (timer.id != etl::timer::id::NO_TIMER)
262 {
263 if (timer.is_active())
264 {
265 ++process_semaphore;
266 active_list.remove(timer.id, false);
267 --process_semaphore;
268 }
269
270 result = true;
271 }
272 }
273
274 return result;
275 }
276
277 //*******************************************
279 //*******************************************
280 bool set_period(etl::timer::id::type id_, uint32_t period_)
281 {
282 if (stop(id_))
283 {
284 timer_array[id_].period = period_;
285 return true;
286 }
287
288 return false;
289 }
290
291 //*******************************************
293 //*******************************************
294 bool set_mode(etl::timer::id::type id_, bool repeating_)
295 {
296 if (stop(id_))
297 {
298 timer_array[id_].repeating = repeating_;
299 return true;
300 }
301
302 return false;
303 }
304
305 protected:
306
307 //*************************************************************************
309 struct timer_data
310 {
311 //*******************************************
312 timer_data()
313 : p_message(ETL_NULLPTR)
314 , p_router(ETL_NULLPTR)
315 , period(0U)
316 , delta(etl::timer::state::INACTIVE)
317 , destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS)
318 , id(etl::timer::id::NO_TIMER)
319 , previous(etl::timer::id::NO_TIMER)
320 , next(etl::timer::id::NO_TIMER)
321 , repeating(true)
322 {
323 }
324
325 //*******************************************
326 timer_data(etl::timer::id::type id_,
327 const etl::imessage& message_,
328 etl::imessage_router& irouter_,
329 uint32_t period_,
330 bool repeating_,
331 etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
332 : p_message(&message_)
333 , p_router(&irouter_)
334 , period(period_)
335 , delta(etl::timer::state::INACTIVE)
336 , destination_router_id(destination_router_id_)
337 , id(id_)
338 , previous(etl::timer::id::NO_TIMER)
339 , next(etl::timer::id::NO_TIMER)
340 , repeating(repeating_)
341 {
342 }
343
344 //*******************************************
346 //*******************************************
347 bool is_active() const
348 {
349 return delta != etl::timer::state::INACTIVE;
350 }
351
352 //*******************************************
354 //*******************************************
355 void set_inactive()
356 {
357 delta = etl::timer::state::INACTIVE;
358 }
359
360 const etl::imessage* p_message;
361 etl::imessage_router* p_router;
362 uint32_t period;
363 uint32_t delta;
364 etl::message_router_id_t destination_router_id;
365 etl::timer::id::type id;
366 uint_least8_t previous;
367 uint_least8_t next;
368 bool repeating;
369
370 private:
371
372 // Disabled.
373 timer_data(const timer_data& other);
374 timer_data& operator =(const timer_data& other);
375 };
376
377 //*******************************************
379 //*******************************************
380 imessage_timer_atomic(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
381 : timer_array(timer_array_)
382 , active_list(timer_array_)
383 , enabled(false)
384 , process_semaphore(0U)
385 , registered_timers(0U)
386 , MAX_TIMERS(MAX_TIMERS_)
387 {
388 }
389
390 //*******************************************
392 //*******************************************
393 ~imessage_timer_atomic()
394 {
395 }
396
397 private:
398
399 //*************************************************************************
401 //*************************************************************************
402 class timer_list
403 {
404 public:
405
406 //*******************************
407 timer_list(timer_data* ptimers_)
408 : head(etl::timer::id::NO_TIMER),
409 tail(etl::timer::id::NO_TIMER),
410 current(etl::timer::id::NO_TIMER),
411 ptimers(ptimers_)
412 {
413 }
414
415 //*******************************
416 bool empty() const
417 {
418 return head == etl::timer::id::NO_TIMER;
419 }
420
421 //*******************************
422 // Inserts the timer at the correct delta position
423 //*******************************
424 void insert(etl::timer::id::type id_)
425 {
426 timer_data& timer = ptimers[id_];
427
428 if (head == etl::timer::id::NO_TIMER)
429 {
430 // No entries yet.
431 head = id_;
432 tail = id_;
433 timer.previous = etl::timer::id::NO_TIMER;
434 timer.next = etl::timer::id::NO_TIMER;
435 }
436 else
437 {
438 // We already have entries.
439 etl::timer::id::type test_id = begin();
440
441 while (test_id != etl::timer::id::NO_TIMER)
442 {
443 timer_data& test = ptimers[test_id];
444
445 // Find the correct place to insert.
446 if (timer.delta <= test.delta)
447 {
448 if (test.id == head)
449 {
450 head = timer.id;
451 }
452
453 // Insert before test.
454 timer.previous = test.previous;
455 test.previous = timer.id;
456 timer.next = test.id;
457
458 // Adjust the next delta to compensate.
459 test.delta -= timer.delta;
460
461 if (timer.previous != etl::timer::id::NO_TIMER)
462 {
463 ptimers[timer.previous].next = timer.id;
464 }
465 break;
466 }
467 else
468 {
469 timer.delta -= test.delta;
470 }
471
472 test_id = next(test_id);
473 }
474
475 // Reached the end?
476 if (test_id == etl::timer::id::NO_TIMER)
477 {
478 // Tag on to the tail.
479 ptimers[tail].next = timer.id;
480 timer.previous = tail;
481 timer.next = etl::timer::id::NO_TIMER;
482 tail = timer.id;
483 }
484 }
485 }
486
487 //*******************************
488 void remove(etl::timer::id::type id_, bool has_expired)
489 {
490 timer_data& timer = ptimers[id_];
491
492 if (head == id_)
493 {
494 head = timer.next;
495 }
496 else
497 {
498 ptimers[timer.previous].next = timer.next;
499 }
500
501 if (tail == id_)
502 {
503 tail = timer.previous;
504 }
505 else
506 {
507 ptimers[timer.next].previous = timer.previous;
508 }
509
510 if (!has_expired)
511 {
512 // Adjust the next delta.
513 if (timer.next != etl::timer::id::NO_TIMER)
514 {
515 ptimers[timer.next].delta += timer.delta;
516 }
517 }
518
519 timer.previous = etl::timer::id::NO_TIMER;
520 timer.next = etl::timer::id::NO_TIMER;
521 timer.delta = etl::timer::state::INACTIVE;
522 }
523
524 //*******************************
525 timer_data& front()
526 {
527 return ptimers[head];
528 }
529
530 //*******************************
531 etl::timer::id::type begin()
532 {
533 current = head;
534 return current;
535 }
536
537 //*******************************
538 etl::timer::id::type previous(etl::timer::id::type last)
539 {
540 current = ptimers[last].previous;
541 return current;
542 }
543
544 //*******************************
545 etl::timer::id::type next(etl::timer::id::type last)
546 {
547 current = ptimers[last].next;
548 return current;
549 }
550
551 //*******************************
552 void clear()
553 {
554 etl::timer::id::type id = begin();
555
556 while (id != etl::timer::id::NO_TIMER)
557 {
558 timer_data& timer = ptimers[id];
559 id = next(id);
560 timer.next = etl::timer::id::NO_TIMER;
561 }
562
563 head = etl::timer::id::NO_TIMER;
564 tail = etl::timer::id::NO_TIMER;
565 current = etl::timer::id::NO_TIMER;
566 }
567
568 private:
569
570 etl::timer::id::type head;
571 etl::timer::id::type tail;
572 etl::timer::id::type current;
573
574 timer_data* const ptimers;
575 };
576
577 // The array of timer data structures.
578 timer_data* const timer_array;
579
580 // The list of active timers.
581 timer_list active_list;
582
583 bool enabled;
584 TSemaphore process_semaphore;
585 uint_least8_t registered_timers;
586
587 public:
588
589 const uint_least8_t MAX_TIMERS;
590 };
591
592 //***************************************************************************
594 //***************************************************************************
595 template <uint_least8_t MAX_TIMERS_, typename TSemaphore>
596 class message_timer_atomic : public etl::imessage_timer_atomic<TSemaphore>
597 {
598 public:
599
600 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
601
602 //*******************************************
604 //*******************************************
605 message_timer_atomic()
606 : imessage_timer_atomic<TSemaphore>(timer_array, MAX_TIMERS_)
607 {
608 }
609
610 private:
611
612 typename etl::imessage_timer_atomic<TSemaphore>::timer_data timer_array[MAX_TIMERS_];
613 };
614}
615
616#endif
617#endif
This is the base of all message routers.
Definition: message_router_generator.h:121
Definition: message.h:69
ETL_CONSTEXPR14 TIterator remove(TIterator first, TIterator last, const T &value)
Definition: algorithm.h:1934
bitset_ext
Definition: absolute.h:38
ETL_CONSTEXPR TContainer::iterator begin(TContainer &container)
Definition: iterator.h:931