diff --git a/equeue.c b/equeue.c index 28c246a..5496ce9 100644 --- a/equeue.c +++ b/equeue.c @@ -484,3 +484,35 @@ void equeue_background(equeue_t *q, q->background.active = true; equeue_mutex_unlock(&q->queuelock); } + +struct equeue_chain_context { + equeue_t *q; + equeue_t *target; + int id; +}; + +static void equeue_chain_dispatch(void *p) { + equeue_dispatch((equeue_t *)p, 0); +} + +static void equeue_chain_update(void *p, int ms) { + struct equeue_chain_context *c = (struct equeue_chain_context *)p; + equeue_cancel(c->target, c->id); + + if (ms >= 0) { + c->id = equeue_call_in(c->target, ms, equeue_chain_dispatch, c->q); + } else { + equeue_dealloc(c->target, c); + } +} + +void equeue_chain(equeue_t *q, equeue_t *target) { + struct equeue_chain_context *c = equeue_alloc(q, + sizeof(struct equeue_chain_context)); + + c->q = q; + c->target = target; + c->id = 0; + + equeue_background(q, equeue_chain_update, c); +} diff --git a/equeue.h b/equeue.h index 624e804..f3aa41b 100644 --- a/equeue.h +++ b/equeue.h @@ -146,10 +146,19 @@ void equeue_cancel(equeue_t *queue, int event); // // The provided update function will be called to indicate when the queue // should be dispatched. A negative timeout will be passed to the update -// function when the timer is no longer needed. +// function when the timer is no longer needed. A null update function +// will disable the existing timer. void equeue_background(equeue_t *queue, void (*update)(void *timer, int ms), void *timer); +// Chain an event queue onto another event queue +// +// After chaining a queue to a target, calling equeue_dispatch on the +// target queue will also dispatch events from this queue. The queues +// will use their own buffers and events are handled independently. +// A null queue as the target will unchain this queue. +void equeue_chain(equeue_t *queue, equeue_t *target); + #ifdef __cplusplus } diff --git a/tests/tests.c b/tests/tests.c index 046179a..78c8392 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -510,6 +510,43 @@ void background_test(void) { test_assert(ms == -1); } +void chain_test(void) { + equeue_t q1; + int err = equeue_create(&q1, 2048); + test_assert(!err); + + equeue_t q2; + err = equeue_create(&q2, 2048); + test_assert(!err); + + equeue_chain(&q2, &q1); + + int touched = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched); + test_assert(id1 && id2); + + id1 = equeue_call(&q1, simple_func, &touched); + id2 = equeue_call(&q2, simple_func, &touched); + test_assert(id1 && id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched); + id2 = equeue_call_in(&q2, 5, simple_func, &touched); + test_assert(id1 && id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched); + id2 = equeue_call_in(&q2, 10, simple_func, &touched); + test_assert(id1 && id2); + + equeue_dispatch(&q1, 30); + + test_assert(touched == 6); +} + // Barrage tests void simple_barrage_test(int N) { equeue_t q; @@ -621,6 +658,7 @@ int main() { test_run(nested_test); test_run(sloth_test); test_run(background_test); + test_run(chain_test); test_run(multithread_test); test_run(simple_barrage_test, 20); test_run(fragmenting_barrage_test, 20);