Skip to content

Commit 7eba7de

Browse files
committed
Limit timer list add/remove operations to in am_timer_tick()
1 parent 8ee28fd commit 7eba7de

File tree

4 files changed

+117
-34
lines changed

4 files changed

+117
-34
lines changed

libs/timer/meson.build

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ libtimer = library(
3434
)
3535

3636
libtimer_dep = declare_dependency(
37-
link_with: [libdlist],
37+
link_with: [libslist],
3838
sources: timer_src,
3939
include_directories : inc)
4040

@@ -43,8 +43,14 @@ libraries += libtimer
4343
if unit_test
4444
e = executable(
4545
'timer',
46-
['timer.c', 'test.c', dlist_src],
47-
dependencies : [libassert_dep, libpal_dep],
46+
['timer.c', 'test.c', slist_src],
47+
c_args: ['-fno-sanitize=all', '-O0', '-fno-trapv', '-fno-lto', '-ggdb'],
48+
dependencies : [
49+
libassert_dep,
50+
libpal_dep,
51+
libonesize_dep,
52+
libevent_dep
53+
],
4854
include_directories: inc)
4955
test('timer', e)
5056
endif

libs/timer/test.c

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@
3232
#include <stddef.h>
3333
#include <string.h>
3434

35+
#include "common/alignment.h"
36+
#include "common/compiler.h"
3537
#include "common/macros.h"
3638
#include "event/event.h"
3739
#include "timer/timer.h"
40+
#include "onesize/onesize.h"
3841
#include "pal/pal.h"
3942

4043
#define EVT_TEST AM_EVT_USER
44+
#define EVT_TEST2 (AM_EVT_USER + 1)
4145

4246
static struct owner {
4347
int npost;
@@ -47,27 +51,82 @@ static struct owner {
4751
static void post_cb(void *owner, const struct am_event *event) {
4852
(void)event;
4953
AM_ASSERT(owner == &m_owner);
50-
AM_ASSERT(AM_EVT_USER == event->id);
54+
AM_ASSERT((EVT_TEST == event->id) || (EVT_TEST2 == event->id));
5155
m_owner.npost++;
5256
}
5357
/* cppcheck-suppress-end constParameterCallback */
5458

5559
static void test_arm(void) {
60+
struct am_event_state_cfg cfg_event = {
61+
.crit_enter = am_pal_crit_enter, .crit_exit = am_pal_crit_exit
62+
};
63+
am_event_state_ctor(&cfg_event);
64+
65+
{
66+
static char
67+
pool[2 * AM_POOL_BLOCK_SIZEOF(struct am_event_timer)] AM_ALIGNED(
68+
AM_ALIGN_MAX
69+
);
70+
am_event_add_pool(
71+
pool,
72+
(int)sizeof(pool),
73+
AM_POOL_BLOCK_SIZEOF(struct am_event_timer),
74+
AM_POOL_BLOCK_ALIGNMENT(AM_ALIGNOF_EVENT)
75+
);
76+
}
77+
5678
memset(&m_owner, 0, sizeof(m_owner));
57-
struct am_timer_state_cfg cfg = {
79+
struct am_timer_state_cfg cfg_timer = {
5880
.post = post_cb,
5981
.publish = NULL,
6082
.update = NULL,
6183
.crit_enter = am_pal_crit_enter,
6284
.crit_exit = am_pal_crit_exit,
6385
};
64-
am_timer_state_ctor(&cfg);
86+
am_timer_state_ctor(&cfg_timer);
6587

6688
struct am_event_timer event;
67-
am_timer_event_ctor(&event, /*id=*/EVT_TEST, /*domain=*/0, &m_owner);
89+
am_timer_event_ctor(
90+
&event, /*id=*/EVT_TEST, AM_PAL_TICK_DOMAIN_DEFAULT, &m_owner
91+
);
6892
am_timer_arm(&event, /*ticks=*/1, /*interval=*/0);
69-
am_timer_tick(/*domain=*/0);
93+
AM_ASSERT(am_timer_is_armed(&event));
94+
95+
struct am_event_timer *event2 = am_timer_event_allocate(
96+
/*id=*/EVT_TEST2,
97+
/*size=*/(int)sizeof(*event2),
98+
AM_PAL_TICK_DOMAIN_DEFAULT,
99+
&m_owner
100+
);
101+
AM_ASSERT(event2);
102+
103+
am_timer_arm(event2, /*ticks=*/1, /*interval=*/0);
104+
AM_ASSERT(am_timer_is_armed(event2));
105+
am_timer_disarm(event2);
106+
AM_ASSERT(!am_timer_is_armed(event2));
107+
AM_ASSERT(!am_timer_domain_is_empty(AM_PAL_TICK_DOMAIN_DEFAULT));
108+
109+
am_timer_tick(AM_PAL_TICK_DOMAIN_DEFAULT);
70110
AM_ASSERT(1 == m_owner.npost);
111+
112+
AM_ASSERT(am_timer_domain_is_empty(AM_PAL_TICK_DOMAIN_DEFAULT));
113+
114+
am_timer_tick(AM_PAL_TICK_DOMAIN_DEFAULT);
115+
AM_ASSERT(1 == m_owner.npost);
116+
117+
am_timer_arm(&event, /*ticks=*/1, /*interval=*/0);
118+
AM_ASSERT(am_timer_is_armed(&event));
119+
120+
am_timer_tick(AM_PAL_TICK_DOMAIN_DEFAULT);
121+
AM_ASSERT(2 == m_owner.npost);
122+
123+
am_timer_arm(event2, /*ticks=*/1, /*interval=*/0);
124+
AM_ASSERT(am_timer_is_armed(event2));
125+
126+
am_timer_tick(AM_PAL_TICK_DOMAIN_DEFAULT);
127+
AM_ASSERT(3 == m_owner.npost);
128+
129+
AM_ASSERT(am_timer_domain_is_empty(AM_PAL_TICK_DOMAIN_DEFAULT));
71130
}
72131

73132
int main(void) {

libs/timer/timer.c

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
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

145159
void 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
}

libs/timer/timer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
#include "common/macros.h"
3333
#include "event/event.h"
34-
#include "dlist/dlist.h"
34+
#include "slist/slist.h"
3535
#include "pal/pal.h"
3636

3737
AM_ASSERT_STATIC(AM_EVENT_TICK_DOMAIN_MASK >= AM_PAL_TICK_DOMAIN_MAX);
@@ -88,7 +88,7 @@ struct am_event_timer {
8888
struct am_event event;
8989

9090
/** to link timer events together */
91-
struct am_dlist_item item;
91+
struct am_slist_item item;
9292

9393
/** the object, who receives the timer event */
9494
void *owner;

0 commit comments

Comments
 (0)