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

Commit bac1fb6

Browse files
committed
Added the ability to background an event queue onto a single-shot timer
The equeue_background function requires only a user-provided update function, which is called to indicate when the queue should be dispatched. The equeue_background function adds a new level of composability to the event queue library. Allowing equeues to be easily backgrounded on hardware timers or even be combined with existing event loops.
1 parent 1b1ed81 commit bac1fb6

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

equeue.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ int equeue_create_inplace(equeue_t *q, size_t size, void *buffer) {
3939
q->generation = 0;
4040
q->breaks = 0;
4141

42+
q->background.active = false;
43+
q->background.update = 0;
44+
q->background.timer = 0;
45+
4246
int err;
4347
err = equeue_sema_create(&q->eventsema);
4448
if (err < 0) {
@@ -68,6 +72,10 @@ void equeue_destroy(equeue_t *q) {
6872
}
6973
}
7074

75+
if (q->background.update) {
76+
q->background.update(q->background.timer, -1);
77+
}
78+
7179
equeue_mutex_destroy(&q->memlock);
7280
equeue_mutex_destroy(&q->queuelock);
7381
equeue_sema_destroy(&q->eventsema);
@@ -199,6 +207,11 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned ms) {
199207
*p = e;
200208
e->ref = p;
201209

210+
if ((q->background.update && q->background.active) &&
211+
(q->queue == e && !e->sibling)) {
212+
q->background.update(q->background.timer, ms);
213+
}
214+
202215
equeue_mutex_unlock(&q->queuelock);
203216

204217
return id;
@@ -312,6 +325,7 @@ void equeue_break(equeue_t *q) {
312325
void equeue_dispatch(equeue_t *q, int ms) {
313326
unsigned tick = equeue_tick();
314327
unsigned timeout = tick + ms;
328+
q->background.active = false;
315329

316330
while (1) {
317331
// collect all the available events and next deadline
@@ -344,6 +358,16 @@ void equeue_dispatch(equeue_t *q, int ms) {
344358
if (ms >= 0) {
345359
deadline = equeue_tickdiff(timeout, tick);
346360
if (deadline <= 0) {
361+
// update background timer if necessary
362+
if (q->background.update) {
363+
equeue_mutex_lock(&q->queuelock);
364+
if (q->background.update && q->queue) {
365+
q->background.update(q->background.timer,
366+
equeue_tickdiff(q->queue->target, tick));
367+
}
368+
q->background.active = true;
369+
equeue_mutex_unlock(&q->queuelock);
370+
}
347371
return;
348372
}
349373
}
@@ -441,3 +465,22 @@ int equeue_call_every(equeue_t *q, int ms, void (*cb)(void*), void *data) {
441465
e->data = data;
442466
return equeue_post(q, ecallback_dispatch, e);
443467
}
468+
469+
// backgrounding
470+
void equeue_background(equeue_t *q,
471+
void (*update)(void *timer, int ms), void *timer) {
472+
equeue_mutex_lock(&q->queuelock);
473+
if (q->background.update) {
474+
q->background.update(q->background.timer, -1);
475+
}
476+
477+
q->background.update = update;
478+
q->background.timer = timer;
479+
480+
if (q->background.update && q->queue) {
481+
q->background.update(q->background.timer,
482+
equeue_tickdiff(q->queue->target, equeue_tick()));
483+
}
484+
q->background.active = true;
485+
equeue_mutex_unlock(&q->queuelock);
486+
}

equeue.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ typedef struct equeue {
5858
unsigned char *data;
5959
} slab;
6060

61+
struct equeue_background {
62+
bool active;
63+
void (*update)(void *timer, int ms);
64+
void *timer;
65+
} background;
66+
6167
equeue_sema_t eventsema;
6268
equeue_mutex_t queuelock;
6369
equeue_mutex_t memlock;
@@ -136,6 +142,14 @@ int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
136142
// stop a currently executing event
137143
void equeue_cancel(equeue_t *queue, int event);
138144

145+
// Background an event queue onto a single-shot timer
146+
//
147+
// The provided update function will be called to indicate when the queue
148+
// should be dispatched. A negative timeout will be passed to the update
149+
// function when the timer is no longer needed.
150+
void equeue_background(equeue_t *queue,
151+
void (*update)(void *timer, int ms), void *timer);
152+
139153

140154
#ifdef __cplusplus
141155
}

tests/tests.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,37 @@ void multithread_test(void) {
479479
equeue_destroy(&q);
480480
}
481481

482+
void background_func(void *p, int ms) {
483+
*(unsigned *)p = ms;
484+
}
485+
486+
void background_test(void) {
487+
equeue_t q;
488+
int err = equeue_create(&q, 2048);
489+
test_assert(!err);
490+
491+
int id = equeue_call_in(&q, 20, pass_func, 0);
492+
test_assert(id);
493+
494+
unsigned ms;
495+
equeue_background(&q, background_func, &ms);
496+
test_assert(ms == 20);
497+
498+
id = equeue_call_in(&q, 10, pass_func, 0);
499+
test_assert(id);
500+
test_assert(ms == 10);
501+
502+
id = equeue_call(&q, pass_func, 0);
503+
test_assert(id);
504+
test_assert(ms == 0);
505+
506+
equeue_dispatch(&q, 0);
507+
test_assert(ms == 10);
508+
509+
equeue_destroy(&q);
510+
test_assert(ms == -1);
511+
}
512+
482513
// Barrage tests
483514
void simple_barrage_test(int N) {
484515
equeue_t q;
@@ -589,6 +620,7 @@ int main() {
589620
test_run(period_test);
590621
test_run(nested_test);
591622
test_run(sloth_test);
623+
test_run(background_test);
592624
test_run(multithread_test);
593625
test_run(simple_barrage_test, 20);
594626
test_run(fragmenting_barrage_test, 20);

0 commit comments

Comments
 (0)