Embedded Template Library 1.0
Loading...
Searching...
No Matches
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_,
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 //*******************************************
307 //*******************************************
308 bool has_active_timer() const
309 {
310 ++process_semaphore;
311 bool result = !active_list.empty();
312 --process_semaphore;
313
314 return result;
315 }
316
317 //*******************************************
320 //*******************************************
321 uint32_t time_to_next() const
322 {
323 uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
324
325 ++process_semaphore;
326 if (!active_list.empty())
327 {
328 delta = active_list.front().delta;
329 }
330 --process_semaphore;
331
332 return delta;
333 }
334
335 protected:
336
337 //*************************************************************************
339 struct timer_data
340 {
341 //*******************************************
342 timer_data()
343 : p_message(ETL_NULLPTR)
344 , p_router(ETL_NULLPTR)
345 , period(0U)
346 , delta(etl::timer::state::Inactive)
347 , destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS)
348 , id(etl::timer::id::NO_TIMER)
349 , previous(etl::timer::id::NO_TIMER)
350 , next(etl::timer::id::NO_TIMER)
351 , repeating(true)
352 {
353 }
354
355 //*******************************************
356 timer_data(etl::timer::id::type id_,
357 const etl::imessage& message_,
358 etl::imessage_router& irouter_,
359 uint32_t period_,
360 bool repeating_,
361 etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
362 : p_message(&message_)
363 , p_router(&irouter_)
364 , period(period_)
365 , delta(etl::timer::state::Inactive)
366 , destination_router_id(destination_router_id_)
367 , id(id_)
368 , previous(etl::timer::id::NO_TIMER)
369 , next(etl::timer::id::NO_TIMER)
370 , repeating(repeating_)
371 {
372 }
373
374 //*******************************************
376 //*******************************************
377 bool is_active() const
378 {
379 return delta != etl::timer::state::Inactive;
380 }
381
382 //*******************************************
384 //*******************************************
385 void set_inactive()
386 {
387 delta = etl::timer::state::Inactive;
388 }
389
390 const etl::imessage* p_message;
391 etl::imessage_router* p_router;
392 uint32_t period;
393 uint32_t delta;
394 etl::message_router_id_t destination_router_id;
396 uint_least8_t previous;
397 uint_least8_t next;
398 bool repeating;
399
400 private:
401
402 // Disabled.
403 timer_data(const timer_data& other);
404 timer_data& operator =(const timer_data& other);
405 };
406
407 //*******************************************
409 //*******************************************
411 : timer_array(timer_array_)
412 , active_list(timer_array_)
413 , enabled(false)
414 , process_semaphore(0U)
415 , registered_timers(0U)
416 , MAX_TIMERS(MAX_TIMERS_)
417 {
418 }
419
420 //*******************************************
422 //*******************************************
424 {
425 }
426
427 private:
428
429 //*************************************************************************
431 //*************************************************************************
432 class timer_list
433 {
434 public:
435
436 //*******************************
437 timer_list(timer_data* ptimers_)
438 : head(etl::timer::id::NO_TIMER)
439 , tail(etl::timer::id::NO_TIMER)
440 , current(etl::timer::id::NO_TIMER)
441 , ptimers(ptimers_)
442 {
443 }
444
445 //*******************************
446 bool empty() const
447 {
448 return head == etl::timer::id::NO_TIMER;
449 }
450
451 //*******************************
452 // Inserts the timer at the correct delta position
453 //*******************************
454 void insert(etl::timer::id::type id_)
455 {
456 timer_data& timer = ptimers[id_];
457
458 if (head == etl::timer::id::NO_TIMER)
459 {
460 // No entries yet.
461 head = id_;
462 tail = id_;
463 timer.previous = etl::timer::id::NO_TIMER;
464 timer.next = etl::timer::id::NO_TIMER;
465 }
466 else
467 {
468 // We already have entries.
469 etl::timer::id::type test_id = begin();
470
471 while (test_id != etl::timer::id::NO_TIMER)
472 {
473 timer_data& test = ptimers[test_id];
474
475 // Find the correct place to insert.
476 if (timer.delta <= test.delta)
477 {
478 if (test.id == head)
479 {
480 head = timer.id;
481 }
482
483 // Insert before test.
484 timer.previous = test.previous;
485 test.previous = timer.id;
486 timer.next = test.id;
487
488 // Adjust the next delta to compensate.
489 test.delta -= timer.delta;
490
491 if (timer.previous != etl::timer::id::NO_TIMER)
492 {
493 ptimers[timer.previous].next = timer.id;
494 }
495 break;
496 }
497 else
498 {
499 timer.delta -= test.delta;
500 }
501
502 test_id = next(test_id);
503 }
504
505 // Reached the end?
506 if (test_id == etl::timer::id::NO_TIMER)
507 {
508 // Tag on to the tail.
509 ptimers[tail].next = timer.id;
510 timer.previous = tail;
511 timer.next = etl::timer::id::NO_TIMER;
512 tail = timer.id;
513 }
514 }
515 }
516
517 //*******************************
518 void remove(etl::timer::id::type id_, bool has_expired)
519 {
520 timer_data& timer = ptimers[id_];
521
522 if (head == id_)
523 {
524 head = timer.next;
525 }
526 else
527 {
528 ptimers[timer.previous].next = timer.next;
529 }
530
531 if (tail == id_)
532 {
533 tail = timer.previous;
534 }
535 else
536 {
537 ptimers[timer.next].previous = timer.previous;
538 }
539
540 if (!has_expired)
541 {
542 // Adjust the next delta.
543 if (timer.next != etl::timer::id::NO_TIMER)
544 {
545 ptimers[timer.next].delta += timer.delta;
546 }
547 }
548
549 timer.previous = etl::timer::id::NO_TIMER;
550 timer.next = etl::timer::id::NO_TIMER;
551 timer.delta = etl::timer::state::Inactive;
552 }
553
554 //*******************************
555 timer_data& front()
556 {
557 return ptimers[head];
558 }
559
560 //*******************************
561 const timer_data& front() const
562 {
563 return ptimers[head];
564 }
565
566 //*******************************
568 {
569 current = head;
570 return current;
571 }
572
573 //*******************************
575 {
576 current = ptimers[last].previous;
577 return current;
578 }
579
580 //*******************************
582 {
583 current = ptimers[last].next;
584 return current;
585 }
586
587 //*******************************
588 void clear()
589 {
591
592 while (id != etl::timer::id::NO_TIMER)
593 {
594 timer_data& timer = ptimers[id];
595 id = next(id);
596 timer.next = etl::timer::id::NO_TIMER;
597 }
598
599 head = etl::timer::id::NO_TIMER;
600 tail = etl::timer::id::NO_TIMER;
601 current = etl::timer::id::NO_TIMER;
602 }
603
604 private:
605
608 etl::timer::id::type current;
609
610 timer_data* const ptimers;
611 };
612
613 // The array of timer data structures.
614 timer_data* const timer_array;
615
616 // The list of active timers.
617 timer_list active_list;
618
619 bool enabled;
620 mutable TSemaphore process_semaphore;
621 uint_least8_t registered_timers;
622
623 public:
624
625 const uint_least8_t MAX_TIMERS;
626 };
627
628 //***************************************************************************
630 //***************************************************************************
631 template <uint_least8_t MAX_TIMERS_, typename TSemaphore>
632 class message_timer_atomic : public etl::imessage_timer_atomic<TSemaphore>
633 {
634 public:
635
636 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
637
638 //*******************************************
640 //*******************************************
643 {
644 }
645
646 private:
647
649 };
650}
651
652#endif
653#endif
This is the base of all message routers.
Definition message_router_generator.h:123
Definition message.h:73
ETL_CONSTEXPR14 TIterator remove(TIterator first, TIterator last, const T &value)
Definition algorithm.h:2180
bitset_ext
Definition absolute.h:38
ETL_CONSTEXPR TContainer::iterator begin(TContainer &container)
Definition iterator.h:962
pair holds two objects of arbitrary type
Definition utility.h:164
ETL_CONSTEXPR pair()
Default constructor.
Definition utility.h:176