Skip to content
This repository was archived by the owner on Aug 19, 2021. It is now read-only.

Commit eb6eee1

Browse files
committed
Fixed small corner cases dispatch logic
- Moved event id updates to deallocation. Dirty events are atomically updated directly to new ids instead of transitioning through a non-dirty state. This fixes behaviour when cancelling events after dispatch and avoids race conditions when cancelling in-flight events. - Added semaphore signal to requeueing of period events. This avoids missing deadline updates from periodic events. - Fixed fabricated range limitation in the posix equeue_sema_wait caused by using the equeue delay type for absolute time values. Tests have been added for the above bugs where possible.
1 parent 43e609e commit eb6eee1

File tree

3 files changed

+69
-14
lines changed

3 files changed

+69
-14
lines changed

equeue.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ static struct equeue_event *equeue_mem_alloc(equeue_t *q, size_t size) {
8686
*p = e->next;
8787
}
8888

89-
e->id += 1;
90-
if (e->id >> (8*sizeof(int)-1 - q->npw2)) {
91-
e->id = 1;
92-
}
93-
9489
equeue_mutex_unlock(&q->memlock);
9590
return e;
9691
}
@@ -240,6 +235,14 @@ static struct equeue_event *equeue_dequeue(equeue_t *q, int *deadline) {
240235
return head;
241236
}
242237

238+
static inline int equeue_incid(equeue_t *q, int id) {
239+
if ((id+1) >> (8*sizeof(int)-1 - q->npw2)) {
240+
return 1;
241+
}
242+
243+
return id+1;
244+
}
245+
243246
int equeue_post(equeue_t *q, void (*cb)(void*), void *p) {
244247
struct equeue_event *e = (struct equeue_event*)p - 1;
245248
int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
@@ -274,6 +277,7 @@ void equeue_cancel(equeue_t *q, int id) {
274277
}
275278

276279
equeue_unqueue(q, e);
280+
e->id = equeue_incid(q, e->id);
277281
equeue_mutex_unlock(&q->queuelock);
278282

279283
equeue_dealloc(q, e+1);
@@ -316,12 +320,15 @@ void equeue_dispatch(equeue_t *q, int ms) {
316320
}
317321

318322
// undirty the id and either dealloc or reenqueue periodic events
319-
e->id = -e->id;
320323
if (e->period >= 0) {
321324
equeue_mutex_lock(&q->queuelock);
325+
e->id = -e->id;
322326
equeue_enqueue(q, e, e->period);
323327
equeue_mutex_unlock(&q->queuelock);
328+
329+
equeue_sema_signal(&q->eventsema);
324330
} else {
331+
e->id = equeue_incid(q, -e->id);
325332
equeue_dealloc(q, e+1);
326333
}
327334
}

equeue_posix.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <time.h>
1414
#include <sys/time.h>
15+
#include <errno.h>
1516

1617

1718
// Tick operations
@@ -57,11 +58,14 @@ bool equeue_sema_wait(equeue_sema_t *s, int ms) {
5758
if (ms < 0) {
5859
return !sem_wait(s);
5960
} else {
60-
ms += equeue_tick();
61+
struct timeval tv;
62+
gettimeofday(&tv, 0);
63+
6164
struct timespec ts = {
62-
.tv_sec = ms/1000,
63-
.tv_nsec = ms*1000000,
65+
.tv_sec = ms/1000 + tv.tv_sec,
66+
.tv_nsec = ms*1000000 + tv.tv_usec*1000,
6467
};
68+
6569
return !sem_timedwait(s, &ts);
6670
}
6771
}

tests/tests.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ void pass_func(void *eh) {
4343
}
4444

4545
void simple_func(void *p) {
46-
*(bool *)p = true;
46+
(*(int *)p)++;
4747
}
4848

4949
struct indirect {
@@ -113,10 +113,10 @@ void simple_call_in_test(void) {
113113
test_assert(!err);
114114

115115
bool touched = false;
116-
int id = equeue_call_in(&q, 5, simple_func, &touched);
116+
int id = equeue_call_in(&q, 10, simple_func, &touched);
117117
test_assert(id);
118118

119-
equeue_dispatch(&q, 10);
119+
equeue_dispatch(&q, 15);
120120
test_assert(touched);
121121

122122
equeue_destroy(&q);
@@ -128,10 +128,10 @@ void simple_call_every_test(void) {
128128
test_assert(!err);
129129

130130
bool touched = false;
131-
int id = equeue_call_every(&q, 5, simple_func, &touched);
131+
int id = equeue_call_every(&q, 10, simple_func, &touched);
132132
test_assert(id);
133133

134-
equeue_dispatch(&q, 10);
134+
equeue_dispatch(&q, 15);
135135
test_assert(touched);
136136

137137
equeue_destroy(&q);
@@ -227,6 +227,34 @@ void cancel_test(int N) {
227227
equeue_destroy(&q);
228228
}
229229

230+
void cancel_unnecessarily_test(void) {
231+
equeue_t q;
232+
int err = equeue_create(&q, 2048);
233+
test_assert(!err);
234+
235+
int id = equeue_call(&q, pass_func, 0);
236+
for (int i = 0; i < 5; i++) {
237+
equeue_cancel(&q, id);
238+
}
239+
240+
id = equeue_call(&q, pass_func, 0);
241+
equeue_dispatch(&q, 0);
242+
for (int i = 0; i < 5; i++) {
243+
equeue_cancel(&q, id);
244+
}
245+
246+
bool touched = false;
247+
equeue_call(&q, simple_func, &touched);
248+
for (int i = 0; i < 5; i++) {
249+
equeue_cancel(&q, id);
250+
}
251+
252+
equeue_dispatch(&q, 0);
253+
test_assert(touched);
254+
255+
equeue_destroy(&q);
256+
}
257+
230258
void loop_protect_test(void) {
231259
equeue_t q;
232260
int err = equeue_create(&q, 2048);
@@ -262,6 +290,20 @@ void break_test(void) {
262290
equeue_destroy(&q);
263291
}
264292

293+
void period_test(void) {
294+
equeue_t q;
295+
int err = equeue_create(&q, 2048);
296+
test_assert(!err);
297+
298+
int count = 0;
299+
equeue_call_every(&q, 10, simple_func, &count);
300+
301+
equeue_dispatch(&q, 55);
302+
test_assert(count == 5);
303+
304+
equeue_destroy(&q);
305+
}
306+
265307
// Barrage tests
266308
void simple_barrage_test(int N) {
267309
equeue_t q;
@@ -365,8 +407,10 @@ int main() {
365407
test_run(destructor_test);
366408
test_run(allocation_failure_test);
367409
test_run(cancel_test, 20);
410+
test_run(cancel_unnecessarily_test);
368411
test_run(loop_protect_test);
369412
test_run(break_test);
413+
test_run(period_test);
370414
test_run(simple_barrage_test, 20);
371415
test_run(fragmenting_barrage_test, 20);
372416
test_run(multithreaded_barrage_test, 20);

0 commit comments

Comments
 (0)