From 90f2b7d7f145630f26eb9bd9f8f4ba00c661e626 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Fri, 5 Aug 2016 17:08:51 -0500 Subject: [PATCH] Fixed underflow issue in deadline calculation Before this fix, it was possible to create a negative deadline in the dispatch function. Other than the fact that this is questionably defined behaviour, very specific circumstances could cause the semaphore to wait indefinitely despite pending events. --- equeue.c | 4 +- tests/tests.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/equeue.c b/equeue.c index 2e1bb5d..b33043a 100644 --- a/equeue.c +++ b/equeue.c @@ -352,7 +352,9 @@ void equeue_dispatch(equeue_t *q, int ms) { equeue_mutex_lock(&q->queuelock); if (q->queue) { int diff = equeue_tickdiff(q->queue->target, tick); - if (deadline < 0 || diff < deadline) { + if (diff <= 0) { + deadline = 0; + } else if (deadline < 0 || diff < deadline) { deadline = diff; } } diff --git a/tests/tests.c b/tests/tests.c index 0db52d6..3fb3a4a 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -46,6 +46,11 @@ void simple_func(void *p) { (*(int *)p)++; } +void sloth_func(void *p) { + usleep(10000); + (*(int *)p)++; +} + struct indirect { int *touched; uint8_t buffer[7]; @@ -102,6 +107,19 @@ void cancel_func(void *p) { equeue_cancel(cancel->q, cancel->id); } +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +void nest_func(void *p) { + struct nest *nest = (struct nest *)p; + equeue_call(nest->q, nest->cb, nest->data); + + usleep(10000); +} + // Simple call tests void simple_call_test(void) { @@ -375,6 +393,92 @@ void period_test(void) { equeue_destroy(&q); } +void nested_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = 0; + struct nest *nest = equeue_alloc(&q, sizeof(struct nest)); + test_assert(nest); + nest->q = &q; + nest->cb = simple_func; + nest->data = &touched; + + int id = equeue_post(&q, nest_func, nest); + test_assert(id); + + equeue_dispatch(&q, 5); + test_assert(touched == 0); + + equeue_dispatch(&q, 5); + test_assert(touched == 1); + + touched = 0; + nest = equeue_alloc(&q, sizeof(struct nest)); + test_assert(nest); + nest->q = &q; + nest->cb = simple_func; + nest->data = &touched; + + id = equeue_post(&q, nest_func, nest); + test_assert(id); + + equeue_dispatch(&q, 20); + test_assert(touched == 1); + + equeue_destroy(&q); +} + +void sloth_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + int touched = 0; + int id = equeue_call(&q, sloth_func, &touched); + test_assert(id); + + id = equeue_call_in(&q, 5, simple_func, &touched); + test_assert(id); + + id = equeue_call_in(&q, 15, simple_func, &touched); + test_assert(id); + + equeue_dispatch(&q, 20); + test_assert(touched == 3); + + equeue_destroy(&q); +} + +void *multithread_thread(void *p) { + equeue_t *q = (equeue_t *)p; + equeue_dispatch(q, -1); + return 0; +} + +void multithread_test(void) { + equeue_t q; + int err = equeue_create(&q, 2048); + test_assert(!err); + + bool touched = false; + equeue_call_every(&q, 1, simple_func, &touched); + + pthread_t thread; + err = pthread_create(&thread, 0, multithread_thread, &q); + test_assert(!err); + + usleep(10000); + equeue_break(&q); + err = pthread_join(thread, 0); + test_assert(!err); + + test_assert(touched); + + equeue_destroy(&q); +} + // Barrage tests void simple_barrage_test(int N) { equeue_t q; @@ -483,6 +587,9 @@ int main() { test_run(loop_protect_test); test_run(break_test); test_run(period_test); + test_run(nested_test); + test_run(sloth_test); + test_run(multithread_test); test_run(simple_barrage_test, 20); test_run(fragmenting_barrage_test, 20); test_run(multithreaded_barrage_test, 20);