3333#include <stddef.h>
3434
3535#include "common/macros.h"
36- #include "dlist/dlist .h"
36+ #include "slist/slist .h"
3737#include "timer/timer.h"
3838#include "pal/pal.h"
3939
@@ -43,8 +43,21 @@ struct am_timer {
4343 * Timer event domains.
4444 * Each domain comprises a list of the timer events,
4545 * which belong to this domain.
46+ * Each domain has a unique tick rate.
4647 */
47- struct am_dlist domains [AM_PAL_TICK_DOMAIN_MAX ];
48+ struct am_slist domains [AM_PAL_TICK_DOMAIN_MAX ];
49+ /**
50+ * Pending timer event domains.
51+ *
52+ * Each armed timer is first placed into corresponding
53+ * list in this array and then moved to struct am_timer::domains
54+ * in next am_timer_tick() call.
55+ *
56+ * This is done to limit struct am_timer::domains[] list operations
57+ * exclusively to am_timer_tick() call to avoid race conditions
58+ * between timer owners the ticker thread/ISR.
59+ */
60+ struct am_slist domains_pend [AM_PAL_TICK_DOMAIN_MAX ];
4861 /** timer module configuration */
4962 struct am_timer_state_cfg cfg ;
5063};
@@ -60,7 +73,8 @@ void am_timer_state_ctor(const struct am_timer_state_cfg *cfg) {
6073 struct am_timer * me = & am_timer_ ;
6174 memset (me , 0 , sizeof (* me ));
6275 for (int i = 0 ; i < AM_COUNTOF (me -> domains ); ++ i ) {
63- am_dlist_init (& me -> domains [i ]);
76+ am_slist_init (& me -> domains [i ]);
77+ am_slist_init (& me -> domains_pend [i ]);
6478 }
6579 me -> cfg = * cfg ;
6680}
@@ -74,7 +88,7 @@ void am_timer_event_ctor(
7488
7589 /* timer events are never deallocated */
7690 memset (event , 0 , sizeof (* event ));
77- am_dlist_item_init (& event -> item );
91+ am_slist_item_init (& event -> item );
7892 event -> event .id = id ;
7993 event -> event .tick_domain = (unsigned )domain & AM_EVENT_TICK_DOMAIN_MASK ;
8094 event -> owner = owner ;
@@ -85,11 +99,6 @@ void am_timer_arm(struct am_event_timer *event, int ticks, int interval) {
8599
86100 AM_ASSERT (event );
87101 AM_ASSERT (AM_EVENT_HAS_USER_ID (event ));
88- /* make sure it wasn't already armed */
89- me -> cfg .crit_enter ();
90- bool armed = am_dlist_item_is_linked (& event -> item );
91- me -> cfg .crit_exit ();
92- AM_ASSERT (!armed );
93102 AM_ASSERT (event -> event .id > 0 );
94103 AM_ASSERT (event -> event .tick_domain < AM_COUNTOF (me -> domains ));
95104 AM_ASSERT (ticks >= 0 );
@@ -103,10 +112,14 @@ void am_timer_arm(struct am_event_timer *event, int ticks, int interval) {
103112
104113 event -> shot_in_ticks = ticks ;
105114 event -> interval_ticks = interval ;
106- event -> event .pubsub_time = event -> owner ? false : true ;
115+ event -> event .pubsub_time = ( event -> owner == NULL ) ;
107116 event -> disarm_pending = 0 ;
108117
109- am_dlist_push_back (& me -> domains [event -> event .tick_domain ], & event -> item );
118+ if (!am_slist_item_is_linked (& event -> item )) {
119+ am_slist_push_back (
120+ & me -> domains_pend [event -> event .tick_domain ], & event -> item
121+ );
122+ }
110123
111124 me -> cfg .crit_exit ();
112125}
@@ -119,7 +132,7 @@ bool am_timer_disarm(struct am_event_timer *event) {
119132
120133 me -> cfg .crit_enter ();
121134
122- bool was_armed = am_dlist_item_is_linked (& event -> item );
135+ bool was_armed = am_slist_item_is_linked (& event -> item );
123136 event -> shot_in_ticks = event -> interval_ticks = 0 ;
124137 event -> disarm_pending = 1 ;
125138
@@ -135,32 +148,34 @@ bool am_timer_is_armed(const struct am_event_timer *event) {
135148
136149 me -> cfg .crit_enter ();
137150
138- bool armed = am_dlist_item_is_linked (& event -> item );
151+ bool armed = am_slist_item_is_linked (& event -> item );
152+ bool disarm_pending = event -> disarm_pending ;
139153
140154 me -> cfg .crit_exit ();
141155
142- return armed ;
156+ return armed && ! disarm_pending ;
143157}
144158
145159void am_timer_tick (int domain ) {
146160 struct am_timer * me = & am_timer_ ;
147161
148162 AM_ASSERT (domain < AM_COUNTOF (me -> domains ));
149163
150- struct am_dlist_iterator it ;
164+ struct am_slist_iterator it ;
151165
152166 me -> cfg .crit_enter ();
153- am_dlist_iterator_init (
154- & me -> domains [domain ], & it , /*direction=*/ AM_DLIST_FORWARD
155- );
167+ if (!am_slist_is_empty (& me -> domains_pend [domain ])) {
168+ am_slist_append (& me -> domains [domain ], & me -> domains_pend [domain ]);
169+ }
170+ am_slist_iterator_init (& me -> domains [domain ], & it );
156171
157- struct am_dlist_item * p = NULL ;
158- while ((p = am_dlist_iterator_next (& it )) != NULL ) {
172+ struct am_slist_item * p = NULL ;
173+ while ((p = am_slist_iterator_next (& it )) != NULL ) {
159174 struct am_event_timer * timer =
160175 AM_CONTAINER_OF (p , struct am_event_timer , item );
161176
162177 if (timer -> disarm_pending ) {
163- am_dlist_iterator_pop (& it );
178+ am_slist_iterator_pop (& it );
164179 timer -> disarm_pending = 0 ;
165180 me -> cfg .crit_exit ();
166181 me -> cfg .crit_enter ();
@@ -183,7 +198,7 @@ void am_timer_tick(int domain) {
183198 if (t -> interval_ticks ) {
184199 t -> shot_in_ticks = t -> interval_ticks ;
185200 } else {
186- am_dlist_iterator_pop (& it );
201+ am_slist_iterator_pop (& it );
187202 }
188203 if (t -> event .pubsub_time ) {
189204 AM_ASSERT (me -> cfg .publish );
@@ -209,6 +224,7 @@ struct am_event_timer *am_timer_event_allocate(
209224 struct am_event_timer * te = (struct am_event_timer * )e ;
210225 AM_ENABLE_WARNING (AM_W_CAST_ALIGN );
211226 am_timer_event_ctor (te , id , domain , owner );
227+
212228 return te ;
213229}
214230
@@ -217,7 +233,9 @@ bool am_timer_domain_is_empty(int domain) {
217233 AM_ASSERT (domain >= 0 );
218234 AM_ASSERT (domain < AM_COUNTOF (me -> domains ));
219235 me -> cfg .crit_enter ();
220- bool empty = am_dlist_is_empty (& me -> domains [domain ]);
236+ bool empty = am_slist_is_empty (& me -> domains [domain ]);
237+ bool empty_pend = am_slist_is_empty (& me -> domains_pend [domain ]);
221238 me -> cfg .crit_exit ();
222- return empty ;
239+
240+ return empty && empty_pend ;
223241}
0 commit comments