diff --git a/.astyleignore b/.astyleignore index 9fdbee2..589f5d9 100644 --- a/.astyleignore +++ b/.astyleignore @@ -19,6 +19,7 @@ ^features/unsupported/ ^hal/storage_abstraction ^platform/cxxsupport +^events/tests/UNITTESTS ^rtos/source/TARGET_CORTEX/rtx4 ^rtos/source/TARGET_CORTEX/rtx5 ^targets diff --git a/.travis.yml b/.travis.yml index e1e6995..ab261ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -100,7 +100,8 @@ - | ! git grep '^#include\s["'"']mbed.h['"'"]$' -- '*.c' '*.h' '*.cpp' '*.hpp' \ ':!*platform_mbed.h' ':!*TESTS/*' ':!TEST_APPS/' ':!UNITTESTS/' \ - ':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*' + ':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*' \ + ':!*events/tests/*' ### Docs Tests ### @@ -252,10 +253,8 @@ connectivity/libraries/ppp connectivity/netsocket features/nanostack features/lwipstack features/frameworks/greentea-client \ features/frameworks/utest features/frameworks/unity components BUILD - python tools/make.py -t GCC_ARM -m NUCLEO_F103RB --source=. --build=BUILD/NUCLEO_F103RB/GCC_ARM -j0 - # Run local equeue tests - - make -C ${EVENTS}/source test # Run profiling tests - - make -C ${EVENTS}/source prof | tee prof + - make -C ${EVENTS}/tests/unit prof | tee prof after_success: # Update status, comparing with master if possible. - | diff --git a/TESTS/events/equeue/main.cpp b/TESTS/events/equeue/main.cpp deleted file mode 100644 index 061b2d9..0000000 --- a/TESTS/events/equeue/main.cpp +++ /dev/null @@ -1,1093 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBED_EXTENDED_TESTS -#error [NOT_SUPPORTED] When running on CI this test is disabled due to limiting testing time. -#else - -#include "utest/utest.h" -#include "unity/unity.h" -#include "greentea-client/test_env.h" - -#include "equeue.h" -#include "mbed.h" - -using namespace utest::v1; - -#define TEST_EQUEUE_SIZE (4*EVENTS_EVENT_SIZE) -#define TEST_THREAD_STACK_SIZE 512 -#define DISPATCH_INFINITE -1 - -// Test functions -static void pass_func(void *eh) -{ -} - -static void simple_func(void *p) -{ - uint8_t *d = reinterpret_cast(p); - if (*d < 255) { - (*d)++; - } -} - -static void sloth_func(void *p) -{ - ThisThread::sleep_for(10); - (*(reinterpret_cast(p)))++; -} - -struct indirect { - uint8_t *touched; - uint8_t buffer[7]; -}; - -static void indirect_func(void *p) -{ - struct indirect *i = reinterpret_cast(p); - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -static void timing_func(void *p) -{ - struct timing *timing = reinterpret_cast(p); - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - TEST_ASSERT_UINT_WITHIN(10, t2, t1); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -static void fragment_func(void *p) -{ - struct fragment *fragment = reinterpret_cast(p); - timing_func(&fragment->timing); - - struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); - TEST_ASSERT_NOT_NULL(nfragment); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - TEST_ASSERT_NOT_EQUAL(0, id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -static void cancel_func(void *p) -{ - struct cancel *ccel = reinterpret_cast(p); - equeue_cancel(ccel->q, ccel->id); -} - -struct nest { - equeue_t *q; - void (*cb)(void *); - void *data; -}; - -static void nest_func(void *p) -{ - struct nest *nst = reinterpret_cast(p); - equeue_call(nst->q, nst->cb, nst->data); - - ThisThread::sleep_for(10); -} - -static void multithread_thread(equeue_t *p) -{ - equeue_dispatch(p, DISPATCH_INFINITE); -} - -static void background_func(void *p, int ms) -{ - *(reinterpret_cast(p)) = ms; -} - -struct ethread { - equeue_t *q; - int ms; -}; - -static void ethread_dispatch(void *p) -{ - struct ethread *t = reinterpret_cast(p); - equeue_dispatch(t->q, t->ms); -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -static void simple_breaker(void *p) -{ - struct count_and_queue *caq = reinterpret_cast(p); - equeue_break(caq->q); - ThisThread::sleep_for(10); - caq->p++; -} - -// Simple call tests - -/** Test that equeue executes function passed by equeue_call. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call is executed properly. - */ -static void test_equeue_simple_call() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_dispatch(&q, 10); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_in. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_in is executed properly. - */ -static void test_equeue_simple_call_in() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int id = equeue_call_in(&q, 10, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 15); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_dispatch(&q, 10); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_every. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_every is executed properly. - */ -static void test_equeue_simple_call_every() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int id = equeue_call_every(&q, 10, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 15); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_post. - * - * Given queue is initialized. - * When the event is posted and after that equeue_dispatch is called. - * Then function passed by equeue_post is executed properly. - */ -static void test_equeue_simple_post() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(i); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, *i->touched); - - equeue_destroy(&q); -} - - -// Misc tests - -/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. - * - * Given queue is initialized. - * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. - * Then functions attached to equeue events destructors are executed properly. - */ -static void test_equeue_destructor() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct indirect *e; - int ids[3]; - - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(3, touched); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - TEST_ASSERT_EQUAL_UINT8(3, touched); - - equeue_dispatch(&q, 0); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_destroy(&q); - TEST_ASSERT_EQUAL_UINT8(3, touched); -} - -/** Test that equeue_alloc returns 0 when equeue can not be allocated. - * - * Given queue is initialized. - * When equeue_alloc is called and equeue can not be allocated - * Then function equeue_alloc returns NULL. - */ -static void test_equeue_allocation_failure() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); - TEST_ASSERT_NULL(p); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - TEST_ASSERT_NULL(p); - - equeue_destroy(&q); -} - -/** Test that equeue does not execute evenets that has been canceled. - * - * Given queue is initialized. - * When events are canceled by equeue_cancel. - * Then they are not executed by calling equeue_dispatch. - */ -template -static void test_equeue_cancel() -{ - equeue_t q; - int err = equeue_create(&q, (N * EVENTS_EVENT_SIZE)); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int ids[N]; - - for (int i = 0; i < N; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, ids[i]); - } - - for (int i = N - 1; i >= 0; i--) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT(0, touched); - - equeue_destroy(&q); -} - -/** Test that events can be cancelled by function executed by equeue_dispatch. - * - * Given queue is initialized. - * When event is cancelled by another event while dispatching. - * Then event that was cancelled is not being executed. - */ -static void test_equeue_cancel_inflight() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - - int id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); - TEST_ASSERT_NOT_NULL(ccel); - ccel->q = &q; - ccel->id = 0; - - id = equeue_post(&q, cancel_func, ccel); - TEST_ASSERT_NOT_EQUAL(0, id); - - ccel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - equeue_destroy(&q); -} - -/** Test that unnecessary canceling events would not affect executing other events. - * - * Given queue is initialized. - * When event is unnecessary canceled by equeue_cancel. - * Then other events are properly executed after calling equeue_dispatch. - */ -static void test_equeue_cancel_unnecessarily() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. - * - * Given queue is initialized. - * When events have 0 ms period time. - * Then dispatching would not end up in infinite loop. - */ -static void test_equeue_loop_protect() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - - touched1 = 0; - uint8_t touched2 = 0; - equeue_call_every(&q, 1, simple_func, &touched2); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break breaks event queue out of dispatching. - * - * Given queue is initialized. - * When equeue_break is called. - * Then event queue will stop dispatching after finisching current dispatching cycle. - */ -static void test_equeue_break() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - uint8_t touched2 = 0; - equeue_call_every(&q, 5, simple_func, &touched2); - - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break function breaks equeue dispatching only once. - * - * Given queue is initialized. - * When equeue_break is called several times. - * Then equeue is stopped only once. - */ -static void test_equeue_break_no_windup() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - touched = 0; - equeue_dispatch(&q, 55); - TEST_ASSERT(touched > 1); - - equeue_destroy(&q); -} - -/** Test that function passed by equeue_call_every is being executed periodically. - * - * Given queue is initialized. - * When function is passed by equeue_call_every with specified period. - * Then event is executed (dispatch time/period) times. - */ -static void test_equeue_period() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - TEST_ASSERT_EQUAL_UINT8(5, touched); - - equeue_destroy(&q); -} - -/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. - * - * Given queue is initialized. - * When nested function is added to enqueue. - * Then it is executed in the next dispatch, or after execution of "mother" function. - */ -static void test_equeue_nested() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - TEST_ASSERT_NOT_NULL(nst); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - int id = equeue_post(&q, nest_func, nst); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 5); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - equeue_dispatch(&q, 1); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - touched = 0; - nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - TEST_ASSERT_NOT_NULL(nst); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - id = equeue_post(&q, nest_func, nst); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 20); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. - * - * Given queue is initialized. - * When sloth function is being called before other functions. - * Then if it is possible all functions start according to predefined schedule correctly. - */ -static void test_equeue_sloth() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - uint8_t touched3 = 0; - int id = equeue_call(&q, sloth_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id); - - id = equeue_call_in(&q, 5, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id); - - id = equeue_call_in(&q, 15, simple_func, &touched3); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 20); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - TEST_ASSERT_EQUAL_UINT8(1, touched3); - - equeue_destroy(&q); -} - -/** Test that equeue can be broken of dispatching from a different thread. - * - * Given queue is initialized. - * When equeue starts dispatching in one thread. - * Then it can be stopped from another thread via equeue_break. - */ -static void test_equeue_multithread() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 1, simple_func, &touched); - - Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); - t1.start(callback(multithread_thread, &q)); - ThisThread::sleep_for(10); - equeue_break(&q); - err = t1.join(); - TEST_ASSERT_EQUAL_INT(0, err); - - TEST_ASSERT(touched > 1); - - equeue_destroy(&q); -} - -/** Test that variable referred via equeue_background shows value in ms to the next event. - * - * Given queue is initialized. - * When variable is referred via equeue_background. - * Then it depicts the time in ms to the next event. - */ -static void test_equeue_background() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - - int ms; - equeue_background(&q, background_func, &ms); - TEST_ASSERT_EQUAL_INT(20, ms); - - id = equeue_call_in(&q, 10, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - TEST_ASSERT_EQUAL_INT(10, ms); - - id = equeue_call(&q, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - TEST_ASSERT_EQUAL_INT(0, ms); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_INT(10, ms); - - equeue_destroy(&q); - TEST_ASSERT_EQUAL_INT(-1, ms); -} - -/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. - * - * Given queue is initialized. - * When target chained equeue is dispatched. - * Then events from both chained equeues are executed. - */ -static void test_equeue_chain() -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - - int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - id1 = equeue_call_in(&q1, 5, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call_in(&q2, 5, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_cancel(&q1, id1); - equeue_cancel(&q2, id2); - - id1 = equeue_call_in(&q1, 10, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call_in(&q2, 10, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 30); - - TEST_ASSERT_EQUAL_UINT8(3, touched1); - TEST_ASSERT_EQUAL_UINT8(3, touched2); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -/** Test that unchaining equeues makes them work on their own. - * - * Given queue is initialized. - * When equeue is unchained. - * Then it can be only dispatched by calling with reference to it. - */ -static void test_equeue_unchain() -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - int id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - int id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_chain(&q2, 0); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_dispatch(&q2, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_chain(&q1, &q2); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q2, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests - -/** Test that equeue keeps good time at starting events. - * - * Given queue is initialized. - * When equeue is being dispatched. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_simple_barrage() -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - TEST_ASSERT_EQUAL_INT(0, err); - - for (int i = 0; i < N; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - TEST_ASSERT_NOT_NULL(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. - * - * Given queue is initialized. - * When equeue is being dispatched and new events are added via already placed in equeue. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_fragmenting_barrage() -{ - equeue_t q; - int err = equeue_create(&q, - 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); - TEST_ASSERT_EQUAL_INT(0, err); - - for (int i = 0; i < N; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); - TEST_ASSERT_NOT_NULL(fragment); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events even if it is working on different thread. - * - * Given queue is initialized. - * When equeue is being dispatched on different thread. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_multithreaded_barrage() -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - TEST_ASSERT_EQUAL_INT(0, err); - - struct ethread t; - t.q = &q; - t.ms = N * 100; - - Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); - - t1.start(callback(ethread_dispatch, &t)); - - for (int i = 0; i < N; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - TEST_ASSERT_NOT_NULL(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - err = t1.join(); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_destroy(&q); -} - -/** Test that break request flag is cleared when equeue stops dispatching timeouts. - * - * Given queue is initialized. - * When equeue break request flag is called but equeue stops dispatching because of timeout. - * Then next equeue dispatch is not stopped. - */ -static void test_equeue_break_request_cleared_on_timeout() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL(0, err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_in(&q, 1, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - - TEST_ASSERT_EQUAL_INT(1, pq.p); - - equeue_cancel(&q, id); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - TEST_ASSERT_EQUAL_UINT8(5, touched); - - equeue_destroy(&q); -} - -/** Test that siblings events don't have next pointers. - * - * Given queue is initialized. - * When events are scheduled on the same time. - * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. - */ -static void test_equeue_sibling() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL(0, err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - TEST_ASSERT_NULL(s->next); - } - } - equeue_cancel(&q, id0); - equeue_cancel(&q, id1); - equeue_cancel(&q, id2); - equeue_destroy(&q); -} - -struct user_allocated_event { - struct equeue_event e; - uint8_t touched; -}; - -/** Test that equeue executes user allocated events passed by equeue_post. - * - * Given queue is initialized and its size is set to store one event at max in its internal memory. - * When post events allocated in queues internal memory (what is done by calling equeue_call). - * Then only one event can be posted due to queue memory size. - * When post user allocated events. - * Then number of posted events is not limited by queue memory size. - * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. - * Then both types of events are executed properly. - */ -static void test_equeue_user_allocated_event_post() -{ - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - TEST_ASSERT_NOT_EQUAL(0, equeue_call_every(&q, 10, simple_func, &touched)); - TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); - TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - equeue_cancel_user_allocated(&q, &e3.e); - equeue_cancel_user_allocated(&q, &e3.e); - - equeue_dispatch(&q, 11); - - TEST_ASSERT_EQUAL_UINT8(1, touched); - TEST_ASSERT_EQUAL_UINT8(1, e1.touched); - TEST_ASSERT_EQUAL_UINT8(1, e2.touched); - TEST_ASSERT_EQUAL_UINT8(0, e3.touched); - TEST_ASSERT_EQUAL_UINT8(1, e4.touched); - TEST_ASSERT_EQUAL_UINT8(1, e5.touched); - - e3.e.target = 10; // set target as it's modified by equeue_call - e3.e.period = 10; // set period as it's reset by equeue_cancel - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_dispatch(&q, 101); - - TEST_ASSERT_EQUAL_UINT8(11, touched); - TEST_ASSERT_EQUAL_UINT8(1, e1.touched); - TEST_ASSERT_EQUAL_UINT8(11, e2.touched); - TEST_ASSERT_EQUAL_UINT8(10, e3.touched); - TEST_ASSERT_EQUAL_UINT8(11, e4.touched); - TEST_ASSERT_EQUAL_UINT8(1, e5.touched); - - equeue_destroy(&q); -} - -Case cases[] = { - Case("simple call test", test_equeue_simple_call), - Case("simple call in test", test_equeue_simple_call_in), - Case("simple call every test", test_equeue_simple_call_every), - Case("simple post test", test_equeue_simple_post), - - Case("destructor test", test_equeue_destructor), - Case("allocation failure test", test_equeue_allocation_failure), - Case("cancel test", test_equeue_cancel<20>), - Case("cancel inflight test", test_equeue_cancel_inflight), - Case("cancel unnecessarily test", test_equeue_cancel_unnecessarily), - Case("loop protect test", test_equeue_loop_protect), - Case("break test", test_equeue_break), - Case("break no windup test", test_equeue_break_no_windup), - Case("period test", test_equeue_period), - Case("nested test", test_equeue_nested), - Case("sloth test", test_equeue_sloth), - - Case("multithread test", test_equeue_multithread), - - Case("background test", test_equeue_background), - Case("chain test", test_equeue_chain), - Case("unchain test", test_equeue_unchain), - - Case("simple barrage test", test_equeue_simple_barrage<20>), - Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>), - Case("multithreaded barrage test", test_equeue_multithreaded_barrage<10>), - Case("break request cleared on timeout test", test_equeue_break_request_cleared_on_timeout), - Case("sibling test", test_equeue_sibling), - Case("user allocated event test", test_equeue_user_allocated_event_post) - -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(40, "default_auto"); - return greentea_test_setup_handler(number_of_cases); -} - -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); - -int main() -{ - Harness::run(specification); -} - -#endif // MBED_EXTENDED_TESTS diff --git a/TESTS/events/queue/main.cpp b/TESTS/events/queue/main.cpp deleted file mode 100644 index 1b03793..0000000 --- a/TESTS/events/queue/main.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed_events.h" -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported -#else - -using namespace utest::v1; - -// Assume that tolerance is 5% of measured time. -#define DELTA(ms) (ms / 20) - -// TEST_EQUEUE_SIZE was reduced below 1024B to fit this test to devices with small RAM (RAM <= 16kB) -// additionally TEST_EQUEUE_SIZE was expressed in EVENTS_EVENT_SIZE to increase readability -// (for more details about EVENTS_EVENT_SIZE see EventQueue constructor) -#define TEST_EQUEUE_SIZE (18*EVENTS_EVENT_SIZE) - -// By empirical, we take 80MHz CPU/2ms delay as base tolerance for time left test. -// For higher CPU frequency, tolerance is fixed to 2ms. -// For lower CPU frequency, tolerance is inversely proportional to CPU frequency. -// E.g.: -// 100MHz: 2ms -// 80MHz: 2ms -// 64MHz: 3ms -// 48MHz: 4ms -#define ALLOWED_TIME_LEFT_TOLERANCE_MS ((SystemCoreClock >= 80000000) ? 2 : ((80000000 * 2 + SystemCoreClock - 1) / SystemCoreClock)) - -// flag for called -volatile bool touched = false; - -// static functions -void func5(int a0, int a1, int a2, int a3, int a4) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3 | a4, 0x1f); -} - -void func4(int a0, int a1, int a2, int a3) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3, 0xf); -} - -void func3(int a0, int a1, int a2) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2, 0x7); -} - -void func2(int a0, int a1) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1, 0x3); -} - -void func1(int a0) -{ - touched = true; - TEST_ASSERT_EQUAL(a0, 0x1); -} - -void func0() -{ - touched = true; -} - -#define SIMPLE_POSTS_TEST(i, ...) \ -void simple_posts_test##i() { \ - EventQueue queue(TEST_EQUEUE_SIZE); \ - \ - touched = false; \ - queue.call(func##i,##__VA_ARGS__); \ - queue.dispatch(0); \ - TEST_ASSERT(touched); \ - \ - touched = false; \ - queue.call_in(1, func##i,##__VA_ARGS__); \ - queue.dispatch(2); \ - TEST_ASSERT(touched); \ - \ - touched = false; \ - queue.call_every(1, func##i,##__VA_ARGS__); \ - queue.dispatch(2); \ - TEST_ASSERT(touched); \ -} - -SIMPLE_POSTS_TEST(5, 0x01, 0x02, 0x04, 0x08, 0x010) -SIMPLE_POSTS_TEST(4, 0x01, 0x02, 0x04, 0x08) -SIMPLE_POSTS_TEST(3, 0x01, 0x02, 0x04) -SIMPLE_POSTS_TEST(2, 0x01, 0x02) -SIMPLE_POSTS_TEST(1, 0x01) -SIMPLE_POSTS_TEST(0) - - -void time_func(Timer *t, int ms) -{ - TEST_ASSERT_INT_WITHIN(DELTA(ms), ms, t->read_ms()); - t->reset(); -} - -template -void call_in_test() -{ - Timer tickers[N]; - - EventQueue queue(TEST_EQUEUE_SIZE); - - for (int i = 0; i < N; i++) { - tickers[i].start(); - queue.call_in((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); - } - - queue.dispatch(N * 100); -} - -template -void call_every_test() -{ - Timer tickers[N]; - - EventQueue queue(TEST_EQUEUE_SIZE); - - for (int i = 0; i < N; i++) { - tickers[i].start(); - queue.call_every((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); - } - - queue.dispatch(N * 100); -} - -void allocate_failure_test() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - int id; - - for (int i = 0; i < 100; i++) { - id = queue.call((void (*)())0); - } - - TEST_ASSERT(!id); -} - -void no() -{ - TEST_ASSERT(false); -} - -template -void cancel_test1() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - - int ids[N]; - - for (int i = 0; i < N; i++) { - ids[i] = queue.call_in(1000, no); - } - - for (int i = N - 1; i >= 0; i--) { - queue.cancel(ids[i]); - } - - queue.dispatch(0); -} - - -// Testing the dynamic arguments to the event class -unsigned counter = 0; - -void count5(unsigned a0, unsigned a1, unsigned a2, unsigned a3, unsigned a5) -{ - counter += a0 + a1 + a2 + a3 + a5; -} - -void count4(unsigned a0, unsigned a1, unsigned a2, unsigned a3) -{ - counter += a0 + a1 + a2 + a3; -} - -void count3(unsigned a0, unsigned a1, unsigned a2) -{ - counter += a0 + a1 + a2; -} - -void count2(unsigned a0, unsigned a1) -{ - counter += a0 + a1; -} - -void count1(unsigned a0) -{ - counter += a0; -} - -void count0() -{ - counter += 0; -} - -void event_class_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - Event e5(&queue, count5); - Event e4(&queue, count5, 1); - Event e3(&queue, count5, 1, 1); - Event e2(&queue, count5, 1, 1, 1); - Event e1(&queue, count5, 1, 1, 1, 1); - Event e0(&queue, count5, 1, 1, 1, 1, 1); - - e5.post(1, 1, 1, 1, 1); - e4.post(1, 1, 1, 1); - e3.post(1, 1, 1); - e2.post(1, 1); - e1.post(1); - e0.post(); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 30); -} - -void event_class_helper_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - Event e5 = queue.event(count5, 1, 1, 1, 1, 1); - Event e4 = queue.event(count4, 1, 1, 1, 1); - Event e3 = queue.event(count3, 1, 1, 1); - Event e2 = queue.event(count2, 1, 1); - Event e1 = queue.event(count1, 1); - Event e0 = queue.event(count0); - - e5.post(); - e4.post(); - e3.post(); - e2.post(); - e1.post(); - e0.post(); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 15); -} - -void event_inference_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - queue.event(count5, 1, 1, 1, 1, 1).post(); - queue.event(count5, 1, 1, 1, 1).post(1); - queue.event(count5, 1, 1, 1).post(1, 1); - queue.event(count5, 1, 1).post(1, 1, 1); - queue.event(count5, 1).post(1, 1, 1, 1); - queue.event(count5).post(1, 1, 1, 1, 1); - - queue.event(callback(count5), 1, 1, 1, 1, 1).post(); - queue.event(callback(count5), 1, 1, 1, 1).post(1); - queue.event(callback(count5), 1, 1, 1).post(1, 1); - queue.event(callback(count5), 1, 1).post(1, 1, 1); - queue.event(callback(count5), 1).post(1, 1, 1, 1); - queue.event(callback(count5)).post(1, 1, 1, 1, 1); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 60); -} - -int timeleft_events[2]; - -void check_time_left(EventQueue *queue, int index, int expected) -{ - const int event_id = timeleft_events[index]; - TEST_ASSERT_INT_WITHIN(ALLOWED_TIME_LEFT_TOLERANCE_MS, expected, queue->time_left(event_id)); - touched = true; -} - -void time_left(EventQueue *queue, int index) -{ - const int event_id = timeleft_events[index]; - TEST_ASSERT_EQUAL(0, queue->time_left(event_id)); -} - -void time_left_test() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - - // Enque check events - TEST_ASSERT(queue.call_in(50, check_time_left, &queue, 0, 100 - 50)); - TEST_ASSERT(queue.call_in(200, check_time_left, &queue, 1, 200 - 200)); - - // Enque events to be checked - timeleft_events[0] = queue.call_in(100, time_left, &queue, 0); - timeleft_events[1] = queue.call_in(200, time_left, &queue, 1); - TEST_ASSERT(timeleft_events[0]); - TEST_ASSERT(timeleft_events[1]); - - queue.dispatch(300); - - // Ensure check was called - TEST_ASSERT(touched); - touched = false; - - int id = queue.call(func0); - TEST_ASSERT(id); - TEST_ASSERT_EQUAL(0, queue.time_left(id)); - queue.dispatch(10); - - // Test invalid event id - TEST_ASSERT_EQUAL(-1, queue.time_left(0)); -} - -void f5(int a1, int a2, int a3, int a4, int a5) -{ - touched = true; -} - -class EventTest { -public: - EventTest() : counter() {} - void f0() - { - counter++; - } - void f1(int a) - { - counter += a; - } - void f5(int a, int b, int c, int d, int e) - { - counter += a + b + c + d + e; - } - uint32_t counter; -}; - -/** Test that queue executes both dynamic and user allocated events. - * - * Given queue is initialized and its size is set to store three Event at max in its internal memory. - * When post queue allocated event. - * Then only three event can be posted due to queue memory size. - * When post user allocated evens. - * Then number of posted events is not limited by queue memory size. - * When both Event and UserAllocatedEvent are posted and queue dispatch is called. - * Then both types of events are executed properly. - * - */ -void mixed_dynamic_static_events_queue_test() -{ - { - EventQueue queue(9 * EVENTS_EVENT_SIZE); - - EventTest e1_test; - Event e1 = queue.event(&e1_test, &EventTest::f0); - e1.delay(10); - e1.period(10); - int id1 = e1.post(); - TEST_ASSERT_NOT_EQUAL(0, id1); - EventTest e2_test; - Event e2 = queue.event(&e2_test, &EventTest::f1, 3); - e2.period(10); - int id2 = e2.post(); - TEST_ASSERT_NOT_EQUAL(0, id2); - EventTest e3_test; - Event e3 = queue.event(&e3_test, &EventTest::f5, 1, 2, 3, 4, 5); - e3.period(10); - int id3 = e3.post(); - TEST_ASSERT_NOT_EQUAL(0, id3); - - - auto ue0 = make_user_allocated_event(func0); - EventTest ue1_test; - auto ue1 = make_user_allocated_event(&ue1_test, &EventTest::f0); - EventTest ue2_test; - auto ue2 = make_user_allocated_event(&ue2_test, &EventTest::f1, 3); - EventTest ue3_test; - auto ue3 = make_user_allocated_event(&ue3_test, &EventTest::f5, 1, 2, 3, 4, 5); - EventTest ue4_test; - auto ue4 = make_user_allocated_event(&ue4_test, &EventTest::f5, 1, 2, 3, 4, 5); - - touched = false; - - ue0.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue0.try_call()); - ue1.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue1.try_call()); - ue2.period(10); - ue2.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue2.try_call()); - ue3.period(10); - ue3.delay(50); - ue3.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue3.try_call()); - ue4.call_on(&queue); - ue4.cancel(); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - ue4.cancel(); - e2.cancel(); - - queue.dispatch(101); - - TEST_ASSERT_EQUAL(true, touched); - TEST_ASSERT_EQUAL(1, ue1_test.counter); - TEST_ASSERT_EQUAL(33, ue2_test.counter); - TEST_ASSERT_EQUAL(90, ue3_test.counter); - TEST_ASSERT_EQUAL(0, ue4_test.counter); - TEST_ASSERT_EQUAL(10, e1_test.counter); - TEST_ASSERT_EQUAL(0, e2_test.counter); - TEST_ASSERT_EQUAL(165, e3_test.counter); - - // user allocated event have to be canceled(removed from the queue) before destruction - // cancel all periodic user events - ue2.cancel(); - ue3.cancel(); - } -} - - -static EventQueue g_queue(0); -static auto ue0 = g_queue.make_user_allocated_event(func0); -static EventTest test1; -static auto ue1 = make_user_allocated_event(&test1, &EventTest::f0); -static EventTest test2; -static auto ue2 = g_queue.make_user_allocated_event(&test2, &EventTest::f1, 3); -static EventTest test3; -static auto ue3 = make_user_allocated_event(&test3, &EventTest::f5, 1, 2, 3, 4, 5); -static EventTest test4; -static auto ue4 = g_queue.make_user_allocated_event(&test4, &EventTest::f5, 1, 2, 3, 4, 5); - - -/** Test that static queue executes user allocated events. - * - * Given static queue is initialized - * When post user allocated evens. - * Then UserAllocatedEvent are posted and dispatched without any error. - */ -void static_events_queue_test() -{ - // check that no dynamic event can be posted - Event e0 = g_queue.event(func0); - TEST_ASSERT_EQUAL(0, e0.post()); - - ue0.delay(100); - ue0.period(200); - - ue1.delay(100); - ue1.period(200); - - ue2.delay(100); - ue2.period(200); - - ue3.delay(100); - ue3.period(200); - - ue4.delay(100); - ue4.period(200); - - ue0.call(); - TEST_ASSERT_EQUAL(false, ue0.try_call()); - ue1.call_on(&g_queue); - TEST_ASSERT_EQUAL(false, ue1.try_call()); - ue2(); - TEST_ASSERT_EQUAL(false, ue2.try_call()); - ue3.call_on(&g_queue); - TEST_ASSERT_EQUAL(false, ue3.try_call()); - ue4.call(); - ue4.cancel(); - ue4.cancel(); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - g_queue.cancel(&ue4); - g_queue.cancel(&ue4); - - g_queue.dispatch(400); - - TEST_ASSERT_EQUAL(2, test1.counter); - TEST_ASSERT_EQUAL(6, test2.counter); - TEST_ASSERT_EQUAL(30, test3.counter); - TEST_ASSERT_EQUAL(0, test4.counter); - - ue4.delay(1); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - g_queue.dispatch(1); - - TEST_ASSERT_EQUAL(2, test1.counter); - TEST_ASSERT_EQUAL(6, test2.counter); - TEST_ASSERT_EQUAL(30, test3.counter); - TEST_ASSERT_EQUAL(15, test4.counter); -} - -// Test setup -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(20, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -const Case cases[] = { - Case("Testing calls with 5 args", simple_posts_test5), - Case("Testing calls with 4 args", simple_posts_test4), - Case("Testing calls with 3 args", simple_posts_test3), - Case("Testing calls with 2 args", simple_posts_test2), - Case("Testing calls with 1 args", simple_posts_test1), - Case("Testing calls with 0 args", simple_posts_test0), - - Case("Testing call_in", call_in_test<20>), - Case("Testing call_every", call_every_test<20>), - - Case("Testing allocate failure", allocate_failure_test), - - Case("Testing event cancel 1", cancel_test1<20>), - Case("Testing the event class", event_class_test), - Case("Testing the event class helpers", event_class_helper_test), - Case("Testing the event inference", event_inference_test), - - Case("Testing time_left", time_left_test), - Case("Testing mixed dynamic & static events queue", mixed_dynamic_static_events_queue_test), - Case("Testing static events queue", static_events_queue_test) - -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/events/timing/main.cpp b/TESTS/events/timing/main.cpp deleted file mode 100644 index fb0b759..0000000 --- a/TESTS/events/timing/main.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed_events.h" -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include -#include - -using namespace utest::v1; - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported -#else - -// Test delay -#ifndef TEST_EVENTS_TIMING_TIME -#define TEST_EVENTS_TIMING_TIME 20000 -#endif - -#ifndef TEST_EVENTS_TIMING_MEAN -#define TEST_EVENTS_TIMING_MEAN 25 -#endif - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338327950288 -#endif - -// Random number generation to skew timing values -float gauss(float mu, float sigma) -{ - float x = (float)rand() / ((float)RAND_MAX + 1); - float y = (float)rand() / ((float)RAND_MAX + 1); - float x2pi = x * 2.0 * M_PI; - float g2rad = sqrt(-2.0 * log(1.0 - y)); - float z = cos(x2pi) * g2rad; - return mu + z * sigma; -} - -float chisq(float sigma) -{ - return pow(gauss(0, sqrt(sigma)), 2); -} - - -Timer timer; -DigitalOut led(LED1); - -equeue_sema_t sema; - -// Timer timing test -void timer_timing_test() -{ - timer.reset(); - timer.start(); - int prev = timer.read_us(); - - while (prev < TEST_EVENTS_TIMING_TIME * 1000) { - int next = timer.read_us(); - if (next < prev) { - printf("backwards drift %d -> %d (%08x -> %08x)\r\n", - prev, next, prev, next); - } - TEST_ASSERT(next >= prev); - prev = next; - } -} - -// equeue tick timing test -void tick_timing_test() -{ - unsigned start = equeue_tick(); - int prev = 0; - - while (prev < TEST_EVENTS_TIMING_TIME) { - int next = equeue_tick() - start; - if (next < prev) { - printf("backwards drift %d -> %d (%08x -> %08x)\r\n", - prev, next, prev, next); - } - TEST_ASSERT(next >= prev); - prev = next; - } -} - -// equeue semaphore timing test -void semaphore_timing_test() -{ - srand(0); - timer.reset(); - timer.start(); - - int err = equeue_sema_create(&sema); - TEST_ASSERT_EQUAL(0, err); - - while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) { - int delay = chisq(TEST_EVENTS_TIMING_MEAN); - - int start = timer.read_us(); - equeue_sema_wait(&sema, delay); - int taken = timer.read_us() - start; - - if (taken < (delay * 1000 - 5000) || taken > (delay * 1000 + 5000)) { - printf("delay %dms => error %dus\r\n", delay, abs(1000 * delay - taken)); - } - - TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000); - - led = !led; - } - - equeue_sema_destroy(&sema); -} - - -// Test setup -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP((number_of_cases + 1)*TEST_EVENTS_TIMING_TIME / 1000, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -const Case cases[] = { - Case("Testing accuracy of timer", timer_timing_test), - Case("Testing accuracy of equeue tick", tick_timing_test), - Case("Testing accuracy of equeue semaphore", semaphore_timing_test), -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/usb_device/basic/USBEndpointTester.cpp b/TESTS/usb_device/basic/USBEndpointTester.cpp index c26bbbb..4b48e94 100644 --- a/TESTS/usb_device/basic/USBEndpointTester.cpp +++ b/TESTS/usb_device/basic/USBEndpointTester.cpp @@ -20,7 +20,7 @@ #include "stdint.h" #include "stdlib.h" #include "USBEndpointTester.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "EndpointResolver.h" #define DEFAULT_CONFIGURATION (1) diff --git a/TESTS/usb_device/basic/USBEndpointTester.h b/TESTS/usb_device/basic/USBEndpointTester.h index a1d02ea..cf649dd 100644 --- a/TESTS/usb_device/basic/USBEndpointTester.h +++ b/TESTS/usb_device/basic/USBEndpointTester.h @@ -21,7 +21,7 @@ /* These headers are included for child class. */ #include "USBDescriptor.h" #include "USBDevice_Types.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "EventFlags.h" #include "platform/NonCopyable.h" diff --git a/TESTS/usb_device/basic/USBTester.cpp b/TESTS/usb_device/basic/USBTester.cpp index 004d03c..743f217 100644 --- a/TESTS/usb_device/basic/USBTester.cpp +++ b/TESTS/usb_device/basic/USBTester.cpp @@ -19,7 +19,7 @@ #include "stdint.h" #include "USBTester.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "EndpointResolver.h" #define DEFAULT_CONFIGURATION (1) diff --git a/TESTS/usb_device/basic/USBTester.h b/TESTS/usb_device/basic/USBTester.h index afe1dfa..d131ab3 100644 --- a/TESTS/usb_device/basic/USBTester.h +++ b/TESTS/usb_device/basic/USBTester.h @@ -21,7 +21,7 @@ /* These headers are included for child class. */ #include "USBDescriptor.h" #include "USBDevice_Types.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "EventFlags.h" #include "platform/NonCopyable.h" diff --git a/UNITTESTS/CMakeLists.txt b/UNITTESTS/CMakeLists.txt index 39a3e18..6f9dbd2 100644 --- a/UNITTESTS/CMakeLists.txt +++ b/UNITTESTS/CMakeLists.txt @@ -103,8 +103,8 @@ # Set include dirs. set(unittest-includes-base "${PROJECT_SOURCE_DIR}/target_h" - "${PROJECT_SOURCE_DIR}/target_h/events" - "${PROJECT_SOURCE_DIR}/target_h/events/equeue" + "${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h" + "${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h/equeue" "${PROJECT_SOURCE_DIR}/target_h/platform" "${PROJECT_SOURCE_DIR}/target_h/platform/cxxsupport" "${PROJECT_SOURCE_DIR}/target_h/drivers" @@ -122,9 +122,9 @@ "${PROJECT_SOURCE_DIR}/../storage/kvstore/kv_config/include" "${PROJECT_SOURCE_DIR}/../drivers" "${PROJECT_SOURCE_DIR}/../hal" - "${PROJECT_SOURCE_DIR}/../events" + "${PROJECT_SOURCE_DIR}/../events/include" + "${PROJECT_SOURCE_DIR}/../events/include/events/internal" "${PROJECT_SOURCE_DIR}/../events/source" - "${PROJECT_SOURCE_DIR}/../events/internal" "${PROJECT_SOURCE_DIR}/../rtos/include" "${PROJECT_SOURCE_DIR}/../features/frameworks" "${PROJECT_SOURCE_DIR}/../features/frameworks/mbed-trace" diff --git a/UNITTESTS/events/equeue/test_equeue.cpp b/UNITTESTS/events/equeue/test_equeue.cpp deleted file mode 100644 index 5d90023..0000000 --- a/UNITTESTS/events/equeue/test_equeue.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/* - * Copyright (c) 2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gtest/gtest.h" -#include "equeue.h" -#include "mbed.h" -#include -#include - -#define EVENTS_EVENT_SIZE (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) -#define TEST_EQUEUE_SIZE 2048 -#define TEST_THREAD_STACK_SIZE 512 -#define DISPATCH_INFINITE -1 -#define ITERATION_TIMES 10 - -extern unsigned int equeue_global_time; - -class TestEqueue : public testing::Test { - virtual void SetUp() - { - } - - virtual void TearDown() - { - } -}; - -// Test functions -static void pass_func(void *eh) -{ -} - -static void simple_func(void *p) -{ - uint8_t *d = reinterpret_cast(p); - if (*d < 255) { - (*d)++; - } -} - -static void sloth_func(void *p) -{ - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; - (*(reinterpret_cast(p)))++; -} - -struct indirect { - uint8_t *touched; - uint8_t buffer[7]; -}; - -static void indirect_func(void *p) -{ - struct indirect *i = reinterpret_cast(p); - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -static void timing_func(void *p) -{ - struct timing *timing = reinterpret_cast(p); - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - EXPECT_TRUE(t2 - 10 < t1 < t2 + 10); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -static void fragment_func(void *p) -{ - struct fragment *fragment = reinterpret_cast(p); - timing_func(&fragment->timing); - - struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); - ASSERT_TRUE(nfragment != NULL); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - ASSERT_NE(0, id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -static void cancel_func(void *p) -{ - struct cancel *ccel = reinterpret_cast(p); - equeue_cancel(ccel->q, ccel->id); -} - -struct nest { - equeue_t *q; - void (*cb)(void *); - void *data; -}; - -static void nest_func(void *p) -{ - struct nest *nst = reinterpret_cast(p); - equeue_call(nst->q, nst->cb, nst->data); - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; -} - -static void *multithread_thread(void *p) -{ - equeue_t *q = reinterpret_cast(p); - equeue_dispatch(q, DISPATCH_INFINITE); - return 0; -} - -class ecount { - mutable pthread_mutex_t mutex; - pthread_cond_t cond; - uint8_t count; -public: - ecount() : count(0) - { - int err = pthread_mutex_init(&mutex, NULL); - EXPECT_EQ(0, err); - err = pthread_cond_init(&cond, NULL); - EXPECT_EQ(0, err); - } - - ~ecount() - { - int err = pthread_mutex_destroy(&mutex); - EXPECT_EQ(0, err); - err = pthread_cond_destroy(&cond); - EXPECT_EQ(0, err); - } - - void lock() const - { - int err = pthread_mutex_lock(&mutex); - EXPECT_EQ(0, err); - } - - void unlock() const - { - int err = pthread_mutex_unlock(&mutex); - EXPECT_EQ(0, err); - } - - void touch() - { - lock(); - if (count < 200) { - count++; - } - unlock(); - int err = pthread_cond_broadcast(&cond); - EXPECT_EQ(0, err); - } - - void wait_for_touches(uint8_t n) - { - lock(); - while (count < n) { - int err = pthread_cond_wait(&cond, &mutex); - EXPECT_EQ(0, err); - } - unlock(); - } -}; - -static void multithread_func(void *p) -{ - static_cast(p)->touch(); -} - -static void background_func(void *p, int ms) -{ - *(reinterpret_cast(p)) = ms; -} - -struct ethread { - pthread_t thread; - equeue_t *q; - int ms; -}; - -static void *ethread_dispatch(void *p) -{ - struct ethread *t = reinterpret_cast(p); - equeue_dispatch(t->q, t->ms); - return 0; -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -static void simple_breaker(void *p) -{ - struct count_and_queue *caq = reinterpret_cast(p); - equeue_break(caq->q); - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; - caq->p++; -} - -// Simple call tests - -/** Test that equeue executes function passed by equeue_call. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_call) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 10); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_in. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_in is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_call_in) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int id = equeue_call_in(&q, 10, simple_func, &touched); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 15); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 10); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_every. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_every is executed properly. - */ - -TEST_F(TestEqueue, test_equeue_simple_call_every) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int id = equeue_call_every(&q, 10, simple_func, &touched); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 15); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_post. - * - * Given queue is initialized. - * When the event is posted and after that equeue_dispatch is called. - * Then function passed by equeue_post is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_post) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(i != NULL); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, *i->touched); - - equeue_destroy(&q); -} - -// Misc tests - -/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. - * - * Given queue is initialized. - * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. - * Then functions attached to equeue events destructors are executed properly. - */ -TEST_F(TestEqueue, test_equeue_destructor) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct indirect *e; - int ids[3]; - - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e != NULL); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(3, touched); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e != NULL); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - ASSERT_NE(0, ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - EXPECT_EQ(3, touched); - - equeue_dispatch(&q, 0); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - ASSERT_NE(0, id); - } - - equeue_destroy(&q); - EXPECT_EQ(3, touched); -} - -/** Test that equeue_alloc returns 0 when equeue can not be allocated. - * - * Given queue is initialized. - * When equeue_alloc is called and equeue can not be allocated - * Then function equeue_alloc returns NULL. - */ -TEST_F(TestEqueue, test_equeue_allocation_failure) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); - EXPECT_TRUE(p == NULL); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - EXPECT_TRUE(p == NULL); - - equeue_destroy(&q); -} - -/** Test that equeue does not execute evenets that has been canceled. - * - * Given queue is initialized. - * When events are canceled by equeue_cancel. - * Then they are not executed by calling equeue_dispatch. - */ -TEST_F(TestEqueue, test_equeue_cancel) -{ - equeue_t q; - int err = equeue_create(&q, (2 * ITERATION_TIMES * EVENTS_EVENT_SIZE)); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int ids[2 * ITERATION_TIMES]; - - for (int i = 0; i < 2 * ITERATION_TIMES; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - ASSERT_NE(0, ids[i]); - } - - for (int i = 2 * ITERATION_TIMES - 1; i >= 0; i--) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that events can be cancelled by function executed by equeue_dispatch. - * - * Given queue is initialized. - * When event is cancelled by another event while dispatching. - * Then event that was cancelled is not being executed. - */ -TEST_F(TestEqueue, test_equeue_cancel_inflight) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - - int id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); - ASSERT_TRUE(ccel != NULL); - ccel->q = &q; - ccel->id = 0; - - id = equeue_post(&q, cancel_func, ccel); - ASSERT_NE(0, id); - - ccel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that unnecessary canceling events would not affect executing other events. - * - * Given queue is initialized. - * When event is unnecessary canceled by equeue_cancel. - * Then other events are properly executed after calling equeue_dispatch. - */ -TEST_F(TestEqueue, test_equeue_cancel_unnecessarily) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. - * - * Given queue is initialized. - * When events have 0 ms period time. - * Then dispatching would not end up in infinite loop. - */ -TEST_F(TestEqueue, test_equeue_loop_protect) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched1); - - touched1 = 0; - uint8_t touched2 = 0; - equeue_call_every(&q, 1, simple_func, &touched2); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break breaks event queue out of dispatching. - * - * Given queue is initialized. - * When equeue_break is called. - * Then event queue will stop dispatching after finisching current dispatching cycle. - */ -TEST_F(TestEqueue, test_equeue_break) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - uint8_t touched2 = 0; - equeue_call_every(&q, 5, simple_func, &touched2); - - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break function breaks equeue dispatching only once. - * - * Given queue is initialized. - * When equeue_break is called several times. - * Then equeue is stopped only once. - */ -TEST_F(TestEqueue, test_equeue_break_no_windup) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 55); - EXPECT_TRUE(touched > 1); - - equeue_destroy(&q); -} - -/** Test that function passed by equeue_call_every is being executed periodically. - * - * Given queue is initialized. - * When function is passed by equeue_call_every with specified period. - * Then event is executed (dispatch time/period) times. - */ - -TEST_F(TestEqueue, test_equeue_period) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - EXPECT_EQ(5, touched); - - equeue_destroy(&q); -} - -/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. - * - * Given queue is initialized. - * When nested function is added to enqueue. - * Then it is executed in the next dispatch, or after execution of "mother" function. - */ -TEST_F(TestEqueue, test_equeue_nested) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - ASSERT_TRUE(nst != NULL); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - int id = equeue_post(&q, nest_func, nst); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 5); - EXPECT_EQ(0, touched); - - equeue_dispatch(&q, 1); - EXPECT_EQ(1, touched); - - touched = 0; - nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - ASSERT_TRUE(nst != NULL); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - id = equeue_post(&q, nest_func, nst); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 20); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. - * - * Given queue is initialized. - * When sloth function is being called before other functions. - * Then if it is possible all functions start according to predefined schedule correctly. - */ -TEST_F(TestEqueue, test_equeue_sloth) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - uint8_t touched3 = 0; - int id = equeue_call(&q, sloth_func, &touched1); - ASSERT_NE(0, id); - - id = equeue_call_in(&q, 5, simple_func, &touched2); - ASSERT_NE(0, id); - - id = equeue_call_in(&q, 15, simple_func, &touched3); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 20); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - EXPECT_EQ(1, touched3); - - equeue_destroy(&q); -} - -/** Test that equeue can be broken of dispatching from a different thread. - * - * Given queue is initialized. - * When equeue starts dispatching in one thread. - * Then it can be stopped from another thread via equeue_break. - */ -TEST_F(TestEqueue, test_equeue_multithread) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - ecount t; - equeue_call_every(&q, 1, multithread_func, &t); - - pthread_t thread; - err = pthread_create(&thread, 0, multithread_thread, &q); - ASSERT_EQ(0, err); - - t.wait_for_touches(1); - equeue_break(&q); - err = pthread_join(thread, 0); - ASSERT_EQ(0, err); - - equeue_destroy(&q); -} - -/** Test that variable referred via equeue_background shows value in ms to the next event. - * - * Given queue is initialized. - * When variable is referred via equeue_background. - * Then it depicts the time in ms to the next event. - */ -TEST_F(TestEqueue, test_equeue_background) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - ASSERT_NE(0, id); - - int ms; - equeue_background(&q, background_func, &ms); - EXPECT_EQ(20, ms); - - id = equeue_call_in(&q, 10, pass_func, 0); - ASSERT_NE(0, id); - EXPECT_EQ(10, ms); - - id = equeue_call(&q, pass_func, 0); - ASSERT_NE(0, id); - EXPECT_EQ(0, ms); - - equeue_dispatch(&q, 0); - EXPECT_EQ(10, ms); - - equeue_destroy(&q); - EXPECT_EQ(-1, ms); -} - -/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. - * - * Given queue is initialized. - * When target chained equeue is dispatched. - * Then events from both chained equeues are executed. - */ -TEST_F(TestEqueue, test_equeue_chain) -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - - int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); - ASSERT_NE(0, id1); - int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); - ASSERT_NE(0, id2); - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - id1 = equeue_call_in(&q1, 5, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call_in(&q2, 5, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_cancel(&q1, id1); - equeue_cancel(&q2, id2); - - id1 = equeue_call_in(&q1, 10, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call_in(&q2, 10, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 30); - - EXPECT_EQ(3, touched1); - EXPECT_EQ(3, touched2); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -/** Test that unchaining equeues makes them work on their own. - * - * Given queue is initialized. - * When equeue is unchained. - * Then it can be only dispatched by calling with reference to it. - */ -TEST_F(TestEqueue, test_equeue_unchain) -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - int id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - int id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_chain(&q2, 0); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_dispatch(&q2, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_chain(&q1, &q2); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q2, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests - -/** Test that equeue keeps good time at starting events. - * - * Given queue is initialized. - * When equeue is being dispatched. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_simple_barrage) -{ - equeue_t q; - int err = equeue_create(&q, 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - ASSERT_EQ(0, err); - - for (int i = 0; i < 2 * ITERATION_TIMES; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - ASSERT_TRUE(timing != NULL); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, 2 * ITERATION_TIMES * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. - * - * Given queue is initialized. - * When equeue is being dispatched and new events are added via already placed in equeue. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_fragmenting_barrage) -{ - equeue_t q; - int err = equeue_create(&q, - 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + ITERATION_TIMES * sizeof(int))); - ASSERT_EQ(0, err); - - for (int i = 0; i < ITERATION_TIMES; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); - ASSERT_TRUE(fragment != NULL); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, ITERATION_TIMES * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events even if it is working on different thread. - * - * Given queue is initialized. - * When equeue is being dispatched on different thread. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_multithreaded_barrage) -{ - equeue_t q; - int err = equeue_create(&q, ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - ASSERT_EQ(0, err); - - struct ethread t; - t.q = &q; - t.ms = ITERATION_TIMES * 100; - - err = pthread_create(&t.thread, 0, ethread_dispatch, &t); - ASSERT_EQ(0, err); - - for (int i = 0; i < ITERATION_TIMES; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - ASSERT_TRUE(timing != NULL); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - ASSERT_NE(0, id); - } - - err = pthread_join(t.thread, 0); - ASSERT_EQ(0, err); - - equeue_destroy(&q); -} - -/** Test that break request flag is cleared when equeue stops dispatching timeouts. - * - * Given queue is initialized. - * When equeue break request flag is called but equeue stops dispatching because of timeout. - * Then next equeue dispatch is not stopped. - */ -TEST_F(TestEqueue, test_equeue_break_request_cleared_on_timeout) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_in(&q, 1, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - - EXPECT_EQ(1, pq.p); - - equeue_cancel(&q, id); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - EXPECT_EQ(5, touched); - - equeue_destroy(&q); -} - -/** Test that siblings events don't have next pointers. - * - * Given queue is initialized. - * When events are scheduled on the same time. - * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. - */ -TEST_F(TestEqueue, test_equeue_sibling) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - EXPECT_TRUE(s->next == NULL); - } - } - equeue_cancel(&q, id0); - equeue_cancel(&q, id1); - equeue_cancel(&q, id2); - equeue_destroy(&q); -} - -/** Test that equeue executes user allocated events passed by equeue_post. - * - * Given queue is initialized and its size is set to store one event at max in its internal memory. - * When post events allocated in queues internal memory (what is done by calling equeue_call). - * Then only one event can be posted due to queue memory size. - * When post user allocated events. - * Then number of posted events is not limited by queue memory size. - * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. - * Then both types of events are executed properly. - */ -TEST_F(TestEqueue, test_equeue_user_allocated_event_post) -{ - struct user_allocated_event { - struct equeue_event e; - uint8_t touched; - }; - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - EXPECT_NE(0, equeue_call_every(&q, 10, simple_func, &touched)); - EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); - EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - equeue_cancel_user_allocated(&q, &e3.e); - equeue_cancel_user_allocated(&q, &e3.e); - - equeue_dispatch(&q, 11); - - EXPECT_EQ(1, touched); - EXPECT_EQ(1, e1.touched); - EXPECT_EQ(1, e2.touched); - EXPECT_EQ(0, e3.touched); - EXPECT_EQ(1, e4.touched); - EXPECT_EQ(1, e5.touched); - - e3.e.target = 10; // set target as it's modified by equeue_call - e3.e.period = 10; // set period as it's reset by equeue_cancel - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_dispatch(&q, 101); - - EXPECT_EQ(11, touched); - EXPECT_EQ(1 , e1.touched); - EXPECT_EQ(11, e2.touched); - EXPECT_EQ(10 , e3.touched); - EXPECT_EQ(11, e4.touched); - EXPECT_EQ(1 , e5.touched); - - equeue_destroy(&q); -} diff --git a/UNITTESTS/events/equeue/unittest.cmake b/UNITTESTS/events/equeue/unittest.cmake deleted file mode 100644 index b685b91..0000000 --- a/UNITTESTS/events/equeue/unittest.cmake +++ /dev/null @@ -1,27 +0,0 @@ - -#################### -# UNIT TESTS -#################### - -list(REMOVE_ITEM unittest-includes ${PROJECT_SOURCE_DIR}/target_h/events ${PROJECT_SOURCE_DIR}/target_h/events/equeue) - -set(unittest-includes ${unittest-includes} - ../events/source - ../events - ../events/internal -) - -set(unittest-sources - ../events/source/equeue.c -) - -set(unittest-test-sources - events/equeue/test_equeue.cpp - stubs/EqueuePosix_stub.c -) - -set(unittest-test-flags - -pthread - -DEQUEUE_PLATFORM_POSIX -) - diff --git a/UNITTESTS/stubs/EqueuePosix_stub.c b/UNITTESTS/stubs/EqueuePosix_stub.c deleted file mode 100644 index e3d975e..0000000 --- a/UNITTESTS/stubs/EqueuePosix_stub.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "internal/equeue_platform.h" - -#if defined(EQUEUE_PLATFORM_POSIX) - -#include - -/* - * Using global variable as a simulation of passing time. Use of sleep functions may cause problems with thread preempting, which can lead to bad timings. - * This problem does not occur on targets. - * This variable is only increased in equeue_sema_wait function and in functions that simulate pass of time. - */ -unsigned int equeue_global_time = 0; - -// Tick operations -void equeue_tick_init(void) {} - -unsigned equeue_tick(void) -{ - return equeue_global_time; -} - - -// Mutex operations -int equeue_mutex_create(equeue_mutex_t *m) -{ - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - return pthread_mutex_init(m, &attr); -} - -void equeue_mutex_destroy(equeue_mutex_t *m) -{ - pthread_mutex_destroy(m); -} - -void equeue_mutex_lock(equeue_mutex_t *m) -{ - pthread_mutex_lock(m); -} - -void equeue_mutex_unlock(equeue_mutex_t *m) -{ - pthread_mutex_unlock(m); -} - - -// Semaphore operations -int equeue_sema_create(equeue_sema_t *s) -{ - int err = pthread_mutex_init(&s->mutex, 0); - if (err) { - return err; - } - - err = pthread_cond_init(&s->cond, 0); - if (err) { - return err; - } - - s->signal = false; - return 0; -} - -void equeue_sema_destroy(equeue_sema_t *s) -{ - pthread_cond_destroy(&s->cond); - pthread_mutex_destroy(&s->mutex); -} - -void equeue_sema_signal(equeue_sema_t *s) -{ - pthread_mutex_lock(&s->mutex); - s->signal = true; - pthread_cond_signal(&s->cond); - pthread_mutex_unlock(&s->mutex); -} - -bool equeue_sema_wait(equeue_sema_t *s, int ms) -{ - pthread_mutex_lock(&s->mutex); - if (!s->signal) { - if (ms < 0) { - pthread_cond_wait(&s->cond, &s->mutex); - } else { - for (int i = 0; i < ms; i++) { - equeue_global_time++; - } - // If ms == 0 increase time so functions don't get stuck in infinite loops. - if (ms == 0) { - equeue_global_time++; - } - } - } - - bool signal = s->signal; - s->signal = false; - pthread_mutex_unlock(&s->mutex); - - return signal; -} - -#endif diff --git a/UNITTESTS/stubs/equeue_stub.c b/UNITTESTS/stubs/equeue_stub.c index 8ae5ab4..125e57c 100644 --- a/UNITTESTS/stubs/equeue_stub.c +++ b/UNITTESTS/stubs/equeue_stub.c @@ -15,7 +15,7 @@ * limitations under the License. */ -#include "equeue.h" +#include "events/equeue.h" #include #include "equeue_stub.h" diff --git a/UNITTESTS/target_h/events/equeue/equeue_platform.h b/UNITTESTS/target_h/events/equeue/equeue_platform.h deleted file mode 100644 index 7d9f968..0000000 --- a/UNITTESTS/target_h/events/equeue/equeue_platform.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EQUEUE_PLATFORM_H -#define EQUEUE_PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef unsigned equeue_mutex_t; - -typedef struct equeue_sema { -} equeue_sema_t; - -#ifdef __cplusplus -} -#endif - -#endif //EQUEUE_PLATFORM_H diff --git a/UNITTESTS/target_h/events/mbed_events.h b/UNITTESTS/target_h/events/mbed_events.h deleted file mode 100644 index c104e73..0000000 --- a/UNITTESTS/target_h/events/mbed_events.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBED_EVENTS_H -#define MBED_EVENTS_H - -#include "events/EventQueue.h" - -#include "events/mbed_shared_queues.h" - -#endif diff --git a/connectivity/cellular/source/framework/device/CellularDevice.cpp b/connectivity/cellular/source/framework/device/CellularDevice.cpp index 266822c..50d900e 100644 --- a/connectivity/cellular/source/framework/device/CellularDevice.cpp +++ b/connectivity/cellular/source/framework/device/CellularDevice.cpp @@ -20,7 +20,7 @@ #include "CellularUtil.h" #include "CellularLog.h" #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" namespace mbed { diff --git a/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp index 95ec2bf..c5a6378 100644 --- a/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp +++ b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp @@ -24,7 +24,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" using namespace std::chrono; diff --git a/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp b/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp index ca97bc6..126fb4f 100644 --- a/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp +++ b/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp @@ -23,7 +23,7 @@ #include "mbed_wait_api.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "smsc9220_emac.h" #include "smsc9220_eth_drv.h" diff --git a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp index 24c3408..9515016 100644 --- a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp @@ -24,7 +24,7 @@ #include "scl_emac.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "scl_buffer_api.h" #include "cy_result.h" #include "cy_pdl.h" diff --git a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp index 138bd8b..63b67bd 100644 --- a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp @@ -24,7 +24,7 @@ #include "whd_emac.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "whd_wlioctl.h" #include "whd_buffer_api.h" #include "cybsp_wifi.h" diff --git a/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp b/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp index 7485284..d7868f1 100644 --- a/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp @@ -40,7 +40,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp b/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp index f08c041..5816bcd 100644 --- a/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp @@ -22,7 +22,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "gd32xx_emac.h" diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp index 945583e..48a1dfe 100644 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp @@ -28,7 +28,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" //#define NU_TRACE diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp index f69d524..8fba334 100644 --- a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp @@ -40,7 +40,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp index 9b0b9df..b8c1414 100644 --- a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp @@ -24,7 +24,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp b/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp index 9ab3557..4b32206 100644 --- a/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp +++ b/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp @@ -18,7 +18,7 @@ #include "cmsis_os.h" #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "lwip/arch.h" #include "lwip/pbuf.h" diff --git a/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp index 6484f53..9355041 100644 --- a/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp +++ b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp @@ -16,7 +16,7 @@ #include "cmsis_os.h" #include "rtos/ThisThread.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "rza1_eth.h" #include "rza1_eth_ext.h" #include "rza1_emac.h" diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index 7ae8276..9068ac6 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -24,7 +24,7 @@ #include "mbed.h" #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "stm32xx_emac_config.h" diff --git a/connectivity/libraries/ppp/source/ppp_service.cpp b/connectivity/libraries/ppp/source/ppp_service.cpp index 58c2470..374bf52 100644 --- a/connectivity/libraries/ppp/source/ppp_service.cpp +++ b/connectivity/libraries/ppp/source/ppp_service.cpp @@ -22,10 +22,10 @@ #include "netsocket/nsapi_types.h" #if PPP_MBED_EVENT_QUEUE -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #else #include "Thread.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #endif #include "mbed_poll.h" diff --git a/connectivity/libraries/ppp/source/ppp_service_if.cpp b/connectivity/libraries/ppp/source/ppp_service_if.cpp index 7b983e2..72b6d79 100644 --- a/connectivity/libraries/ppp/source/ppp_service_if.cpp +++ b/connectivity/libraries/ppp/source/ppp_service_if.cpp @@ -20,7 +20,7 @@ #include "mbed_interface.h" #include "mbed_trace.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "mbed_poll.h" #include "ip4string.h" #include "ip6string.h" diff --git a/connectivity/netsocket/source/NetworkStack.cpp b/connectivity/netsocket/source/NetworkStack.cpp index f9d0c20..be582a9 100644 --- a/connectivity/netsocket/source/NetworkStack.cpp +++ b/connectivity/netsocket/source/NetworkStack.cpp @@ -19,7 +19,7 @@ #include "stddef.h" #include #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "platform/mbed_error.h" // Default NetworkStack operations diff --git a/connectivity/netsocket/source/nsapi_dns.cpp b/connectivity/netsocket/source/nsapi_dns.cpp index 4b639bc..dacce85 100644 --- a/connectivity/netsocket/source/nsapi_dns.cpp +++ b/connectivity/netsocket/source/nsapi_dns.cpp @@ -24,7 +24,7 @@ #include #include #include -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "events/EventQueue.h" #include "netsocket/OnboardNetworkStack.h" #include "rtos/Kernel.h" diff --git a/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp b/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp index e0c3680..db0b981 100644 --- a/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp +++ b/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp @@ -22,7 +22,7 @@ #include "netsocket/SocketAddress.h" #include "EMAC_mock.h" #include "OnboardNetworkStack_mock.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include #include #include diff --git a/drivers/USBCDC_ECM.h b/drivers/USBCDC_ECM.h index b29e051..70ddb9a 100644 --- a/drivers/USBCDC_ECM.h +++ b/drivers/USBCDC_ECM.h @@ -24,7 +24,7 @@ #include "ByteBuffer.h" #include "Mutex.h" #include "EventFlags.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "Thread.h" #include "Callback.h" diff --git a/events/Event.h b/events/Event.h deleted file mode 100644 index 8e13c48..0000000 --- a/events/Event.h +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EVENT_H -#define EVENT_H - -#include -#include "events/EventQueue.h" -#include "platform/mbed_assert.h" - -namespace events { -/** \defgroup events-public-api Events - * \ingroup mbed-os-public - * @{ - */ - -/** Event - * - * Representation of an event for fine-grain dispatch control - */ -template -class Event; - -/** - * \defgroup events_Event Event class - * @{ - */ - -/** Event - * - * Representation of an event for fine-grain dispatch control - */ -template -class Event { -public: - using duration = std::chrono::duration; - - /** Create an event - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param q Event queue to dispatch on - * @param f Function to execute when the event is dispatched - */ - template - Event(EventQueue *q, F f) - { - _event = static_cast( - equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); - - if (_event) { - _event->equeue = &q->_equeue; - _event->id = 0; - _event->delay = duration(0); - _event->period = duration(-1); - - _event->post = &Event::event_post; - _event->dtor = &Event::event_dtor; - - new (_event + 1) F(std::move(f)); - - _event->ref = 1; - } - } - - /** Copy constructor for events - */ - Event(const Event &e) - { - _event = 0; - if (e._event) { - _event = e._event; - _event->ref += 1; - } - } - - /** Assignment operator for events - */ - Event &operator=(const Event &that) - { - if (this != &that) { - this->~Event(); - new (this) Event(that); - } - - return *this; - } - - /** Destructor for events - */ - ~Event() - { - if (_event) { - _event->ref -= 1; - if (_event->ref == 0) { - _event->dtor(_event); - equeue_dealloc(_event->equeue, _event); - } - } - } - - /** Configure the delay of an event - * - * @param d Millisecond delay before dispatching the event - */ - void delay(duration d) - { - if (_event) { - _event->delay = d; - } - } - - /** Configure the delay of an event - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - * - * @param d Millisecond delay before dispatching the event - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - void delay(int d) - { - delay(duration(d)); - } - - /** Configure the period of an event - * - * @param p Millisecond period for repeatedly dispatching an event - */ - void period(duration p) - { - if (_event) { - _event->period = p; - } - } - - /** Configure the period of an event - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - * - * @param p Millisecond period for repeatedly dispatching an event - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - void period(int p) - { - period(duration(p)); - } - - /** Posts an event onto the underlying event queue - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param args Arguments to pass to the event - * @return A unique id that represents the posted event and can - * be passed to EventQueue::cancel, or an id of 0 if - * there is not enough memory to allocate the event. - */ - int post(ArgTs... args) const - { - if (!_event) { - return 0; - } - - _event->id = _event->post(_event, args...); - return _event->id; - } - - /** Posts an event onto the underlying event queue, returning void - * - * @param args Arguments to pass to the event - */ - void call(ArgTs... args) const - { - MBED_UNUSED int id = post(args...); - MBED_ASSERT(id); - } - - /** Posts an event onto the underlying event queue, returning void - * - * @param args Arguments to pass to the event - */ - void operator()(ArgTs... args) const - { - return call(args...); - } - - /** Static thunk for passing as C-style function - * - * @param func Event to call passed as a void pointer - * @param args Arguments to pass to the event - */ - static void thunk(void *func, ArgTs... args) - { - return static_cast(func)->call(args...); - } - - /** Cancels the most recently posted event - * - * Attempts to cancel the most recently posted event. It is safe to call - * cancel after an event has already been dispatched. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active, the cancel - * function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. - */ - void cancel() const - { - if (_event) { - equeue_cancel(_event->equeue, _event->id); - } - } - -private: - struct event { - unsigned ref; - equeue_t *equeue; - int id; - - duration delay; - duration period; - - int (*post)(struct event *, ArgTs... args); - void (*dtor)(struct event *); - - // F follows - } *_event; - - // Event attributes - template - static int event_post(struct event *e, ArgTs... args) - { - typedef EventQueue::context C; - void *p = equeue_alloc(e->equeue, sizeof(C)); - if (!p) { - return 0; - } - - new (p) C(*(F *)(e + 1), args...); - equeue_event_delay(p, e->delay.count()); - equeue_event_period(p, e->period.count()); - equeue_event_dtor(p, &EventQueue::function_dtor); - return equeue_post(e->equeue, &EventQueue::function_call, p); - } - - template - static void event_dtor(struct event *e) - { - ((F *)(e + 1))->~F(); - } - -public: - /** Create an event - * @param q Event queue to dispatch on - * @param f Function to execute when the event is dispatched - * @param context_args Arguments to bind to the callback, these arguments are - * allocated on an IRQ-safe allocator from the event queue's - * memory pool. Must be type-compatible with bound_args, the - * arguments to the underlying callback. - */ - template - Event(EventQueue *q, F f, ContextArgTs... context_args) : - Event(q, EventQueue::context(f, context_args...)) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, ArgTs...), B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, ArgTs...) const, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, ArgTs...), B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, ArgTs...) const, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, ArgTs...), B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } -}; - -/** @}*/ - - -// Convenience functions declared here to avoid cyclic -// dependency between Event and EventQueue -template -Event EventQueue::event(R(*func)(ArgTs...)) -{ - return Event(this, func); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(ArgTs...)) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(ArgTs...) const) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(ArgTs...) volatile) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(mbed::Callback cb) -{ - return Event(this, cb); -} - -template -Event EventQueue::event(R(*func)(B0, ArgTs...), C0 c0) -{ - return Event(this, func, c0); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0) -{ - return Event(this, cb, c0); -} - -template -Event EventQueue::event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1) -{ - return Event(this, func, c0, c1); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1) -{ - return Event(this, cb, c0, c1); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) -{ - return Event(this, func, c0, c1, c2); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2) -{ - return Event(this, cb, c0, c1, c2); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, func, c0, c1, c2, c3); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, cb, c0, c1, c2, c3); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, func, c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, cb, c0, c1, c2, c3, c4); -} - -/** @}*/ -} -#endif diff --git a/events/EventQueue.h b/events/EventQueue.h deleted file mode 100644 index 1bcc018..0000000 --- a/events/EventQueue.h +++ /dev/null @@ -1,1512 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EVENT_QUEUE_H -#define EVENT_QUEUE_H - -#include "events/equeue.h" -#include "platform/Callback.h" -#include "platform/NonCopyable.h" -#include -#include -#include -#include - -namespace events { -/** - * \addtogroup events-public-api - * @{ - */ - -/** EVENTS_EVENT_SIZE - * Minimum size of an event - * This size fits a Callback at minimum - */ -#define EVENTS_EVENT_SIZE \ - (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) - -/** EVENTS_QUEUE_SIZE - * Default size of buffer for events - */ -#define EVENTS_QUEUE_SIZE (32*EVENTS_EVENT_SIZE) - -// Predeclared classes -template -class Event; -template -class UserAllocatedEvent; - -/** - * \defgroup events_EventQueue EventQueue class - * @{ - */ - -/** EventQueue - * - * Flexible event queue for dispatching events - */ -class EventQueue : private mbed::NonCopyable { -public: - using duration = std::chrono::duration; - - /** Create an EventQueue - * - * Create an event queue. The event queue either allocates a buffer of - * the specified size with malloc or uses the user provided buffer or - * uses 1B dummy buffer if 0 size passed. - * - * 0 size queue is a special purpose queue to dispatch static events - * only (see UserAllocatedEvent). Such a queue gives the guarantee - * that no dynamic memory allocation will take place while queue - * creation and events posting & dispatching. - * - * @param size Size of buffer to use for events in bytes - * (default to EVENTS_QUEUE_SIZE) - * If 0 provided then 1B dummy buffer is used - * @param buffer Pointer to buffer to use for events - * (default to NULL) - */ - EventQueue(unsigned size = EVENTS_QUEUE_SIZE, unsigned char *buffer = NULL); - - /** Destroy an EventQueue - */ - ~EventQueue(); - - /** Dispatch events - * - * Executes events until the specified milliseconds have passed. - * If ms is negative, the dispatch function will dispatch events - * indefinitely or until break_dispatch is called on this queue. - * - * When called with a finite timeout, the dispatch function is guaranteed - * to terminate. When called with a timeout of 0, the dispatch function - * does not wait and is IRQ safe. - * - * @param ms Time to wait for events in milliseconds, a negative - * value will dispatch events indefinitely - * (default to -1) - */ - void dispatch(int ms = -1); - - /** Dispatch events without a timeout - * - * This is equivalent to EventQueue::dispatch with no arguments, but - * avoids overload ambiguities when passed as a callback. - * - * @see EventQueue::dispatch - */ - void dispatch_forever() - { - dispatch(); - } - - /** Break out of a running event loop - * - * Forces the specified event queue's dispatch loop to terminate. Pending - * events may finish executing, but no new events will be executed. - */ - void break_dispatch(); - - /** Millisecond counter - * - * Returns the underlying tick of the event queue represented as the - * number of milliseconds that have passed since an arbitrary point in - * time. Intentionally overflows to 0 after 2^32-1. - * - * @return The underlying tick of the event queue in milliseconds - */ - unsigned tick(); - - /** Cancel an in-flight event - * - * Attempts to cancel an event referenced by the unique id returned from - * one of the call functions. It is safe to call cancel after an event - * has already been dispatched. - * - * id must be valid i.e. event must have not finished executing. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active in another thread, - * the cancel function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. A call made from - * the same thread as the dispatch loop will always succeed with a valid id. - * - * @param id Unique id of the event - * @return true if event was successfully cancelled - * false if event was not cancelled (invalid id or executing already begun) - */ - bool cancel(int id); - - /** Cancel an in-flight user allocated event - * - * Attempts to cancel an UserAllocatedEvent referenced by its address - * It is safe to call cancel after an event has already been dispatched. - * - * Event must be valid i.e. event must have not finished executing - * and must have been bound to this queue. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active in another thread, - * the cancel function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. A call made from - * the same thread as the dispatch loop will always succeed with a valid id. - * - * @param event Address of the event - * @return true if event was successfully cancelled - * false if event was not cancelled (invalid queue or executing already begun) - */ - template - bool cancel(UserAllocatedEvent *event) - { - if (event->_equeue != &_equeue) { - return false; - } - return equeue_cancel_user_allocated(&_equeue, event); - } - - /** Query how much time is left for delayed event - * - * If the event is delayed, this function can be used to query how much time - * is left until the event is due to be dispatched. - * - * id must be valid i.e. event must have not finished executing. - * - * This function is IRQ safe. - * - * @param id Unique id of the event - * - * @return Remaining time in milliseconds or - * 0 if event is already due to be dispatched or - * is currently executing. - * Undefined if id is invalid. - * - */ - int time_left(int id); - - /** Query how much time is left for delayed UserAllocatedEvent - * - * If the event is delayed, this function can be used to query how much time - * is left until the event is due to be dispatched. - * - * Event must be valid i.e. event must have not finished executing - * and must have been bound to this queue. - * - * This function is IRQ safe. - * - * @param event Address of the event - * - * @return Remaining time in milliseconds or - * 0 if event is already due to be dispatched or - * is currently executing. - * Undefined if id is invalid. - * - */ - template - int time_left(UserAllocatedEvent *event) - { - if (event && event->_equeue != &_equeue) { - return -1; - } - return equeue_timeleft_user_allocated(&_equeue, &event->_e); - } - - /** Background an event queue onto a single-shot timer-interrupt - * - * When updated, the event queue will call the provided update function - * with a timeout indicating when the queue should be dispatched. A - * negative timeout will be passed to the update function when the - * timer-interrupt is no longer needed. - * - * Passing a null function disables the existing update function. - * - * The background function allows an event queue to take advantage of - * hardware timers or other event loops, allowing an event queue to be - * ran in the background without consuming the foreground thread. - * - * @param update Function called to indicate when the queue should be - * dispatched - */ - void background(mbed::Callback update); - - /** Chain an event queue onto another event queue - * - * After chaining a queue to a target, calling dispatch on the target - * queue will also dispatch events from this queue. The queues use - * their own buffers and events must be handled independently. - * - * A null queue as the target will unchain the existing queue. - * - * The chain function allows multiple event queues to be composed, - * sharing the context of a dispatch loop while still being managed - * independently - * - * @param target Queue that will dispatch this queue's events as a - * part of its dispatch loop - * - * @return Zero on success and negative error code value if chaining fails - * - */ - int chain(EventQueue *target); - - - -#if defined(DOXYGEN_ONLY) - /** Calls an event on the queue - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - * Returned id will remain valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks - * queue.call(printf, "called immediately\n"); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call(F f, Args ...args); - - /** Calls an event on the queue - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * Returned ID remains valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(1); - * - * // events are simple callbacks, call object method - * // with provided parameter - * queue.call(&handler_cb, &EventHandler::handler, 2); - * - * // the dispath method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call(T *obj, R (T::*method)(Args ...args), Args ...args); - // *INDENT-ON* - - /** Calls an event on the queue after a specified delay - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks - * queue.call_in(2s, printf, "called in 2 seconds\n"); - * - * // the dispatch methods executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call_in(duration ms, F f, ArgTs ...args); - - /** Calls an event on the queue after a specified delay - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(3); - * - * // events are simple callbacks, call object method in 2 seconds - * // with provided parameter - * queue.call_in(2s, &handler_cb, &EventHandler::handler, 4); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call_in(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); - // *INDENT-ON* - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param ms Period of the event in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks, call every 2 seconds - * queue.call_every(2s, printf, "Calling every 2 seconds\n"); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call_every(duration ms, F f, ArgTs ...args); - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param ms Period of the event in milliseconds - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(5); - * - * // events are simple callbacks, call object method every 2 seconds - * // with provided parameter - * queue.call_every(2s, &handler_cb, &EventHandler::handler, 6); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call_every(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param func Function to execute when the event is dispatched - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * void handler(int c) { - * printf("Param: %d\r\n", c); - * } - * - * int main() - * { - * EventQueue queue; - * - * // Create event with parameter - * Event e = queue.event(handler, 1); - * e(); - * - * // Create event and post parameter later - * Event e2 = queue.event(handler); - * - * // Post the event with paramter 8 - * e.post(8); - * - * // The dispatch method executes events - * queue.dispatch(); - * - * e2.post(2); - * - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - Event event(R (*func)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * class EventHandler { - * int _id; - * - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() - * { - * EventQueue queue; - * - * EventHandler handler_cb(10); - * - * // Create event on the eventqueue with a method callback - * Event e = queue.event(&handler_cb, &EventHandler::handler); - * - * // Post the event with paramter 8 - * e.post(11); - * - * // The dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - Event event(T *obj, R (T::*method)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param cb Callback object - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * void handler(int c) { - * printf("Param: %d\r\n", c); - * } - * - * int main() - * { - * EventQueue queue; - * // Create callback object acting as a function - * // pointer to handler - * Callback cb(handler); - * - * // Pass the callback object to the eventqueue - * Event e = queue.event(cb); - * - * // Post the event with parameter 8 - * e.post(9); - * - * // The dispatch method executes events - * q.dispatch(); - * } - * @endcode - */ - template - Event event(mbed::Callback cb, ContextArgTs ...context_args); - - /** Creates an user allocated event bound to the event queue - * - * Constructs an user allocated event bound to the specified event queue. - * The specified callback acts as the target for the event and is executed - * in the context of the event queue's dispatch loop once posted. - * - * @code - * #include "mbed.h" - * - * void handler(int data) { ... } - * - * class Device { - * public: - * void handler(int data) { ... } - * }; - * - * Device dev; - * - * // queue with not internal storage for dynamic events - * // accepts only user allocated events - * static EventQueue queue(0); - * // Create events - * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); - * static auto e2 = queue.make_user_allocated_event(handler, 3); - * - * int main() - * { - * e1.call_on(&queue); - * e2.call(); - * - * queue.dispatch(1); - * } - * @endcode - * - * @param f Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); - - -#else - - /** Calls an event on the queue - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * Returned ID remains valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * int main() - * { - * EventQueue queue; - * - * Callback cb(handler); - * - * // Create event on the eventqueue with a separate callback object - * Event e = queue.event(cb); - * e.post(1); - * queue.dispatch(); - * } - * @endcode - */ - template - int call(F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - - /** Calls an event on the queue - * @see EventQueue::call - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - int call(F f, ArgTs... args) - { - return call(context(std::move(f), args...)); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - int call_in(duration ms, F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_delay(e, ms.count()); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - int call_in(duration ms, F f, ArgTs... args) - { - return call_in(ms, context(std::move(f), args...)); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, F f) - { - return call_in(duration(ms), std::move(f)); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, F f, ArgTs... args) - { - return call_in(duration(ms), std::move(f), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param ms Period of the event in milliseconds - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - int call_every(duration ms, F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_delay(e, ms.count()); - equeue_event_period(e, ms.count()); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @param ms Period of the event in milliseconds - */ - template - int call_every(duration ms, F f, ArgTs... args) - { - return call_every(ms, context(std::move(f), args...)); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param ms Period of the event in milliseconds - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, F f) - { - return call_every(duration(ms), std::move(f)); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @param ms Period of the event in milliseconds - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, F f, ArgTs... args) - { - return call_every(duration(ms), std::move(f), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param func Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - Event event(R(*func)(ArgTs...)); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(ArgTs...)); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(ArgTs...) const); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(ArgTs...) volatile); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, ArgTs...), C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an user allocated event bound to the event queue - * - * Constructs an user allocated event bound to the specified event queue. - * The specified callback acts as the target for the event and is executed - * in the context of the event queue's dispatch loop once posted. - * - * @param f Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args); -#endif - -protected: -#if !defined(DOXYGEN_ONLY) - template - friend class Event; - template - friend class UserAllocatedEvent; - struct equeue _equeue; - mbed::Callback _update; - - // Function attributes - template - static void function_call(void *p) - { - (*(F *)p)(); - } - - template - static void function_dtor(void *p) - { - ((F *)p)->~F(); - } - - // Context structures - template - struct context; - - template - struct context { - F f; - - constexpr context(F f) - : f(f) {} - - template - void operator()(ArgTs... args) - { - f(args...); - } - }; - - template - struct context { - F f; - C0 c0; - - constexpr context(F f, C0 c0) - : f(f), c0(c0) {} - - template - void operator()(ArgTs... args) - { - f(c0, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - - constexpr context(F f, C0 c0, C1 c1) - : f(f), c0(c0), c1(c1) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - - constexpr context(F f, C0 c0, C1 c1, C2 c2) - : f(f), c0(c0), c1(c1), c2(c2) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - C3 c3; - - constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3) - : f(f), c0(c0), c1(c1), c2(c2), c3(c3) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, c3, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - C3 c3; - C4 c4; - - constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) - : f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, c3, c4, args...); - } - }; -#endif //!defined(DOXYGEN_ONLY) -}; - -/** @}*/ -/** @}*/ - -} - -#endif diff --git a/events/UserAllocatedEvent.h b/events/UserAllocatedEvent.h deleted file mode 100644 index 94dd360..0000000 --- a/events/UserAllocatedEvent.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (c) 2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef USER_ALLOCATED_EVENT_H -#define USER_ALLOCATED_EVENT_H - -#include "events/EventQueue.h" -#include "platform/mbed_assert.h" -#include "platform/mbed_atomic.h" - -namespace events { -/** - * \addtogroup events-public-api - * @{ - */ -template -class UserAllocatedEvent; - -/** - * \defgroup events_Event UserAllocatedEvent class - * @{ - */ - -/** UserAllocatedEvent - * - * Representation of an static event for fine-grain dispatch control. - * - * UserAllocatedEvent provides mechanism for event posting and dispatching - * without utilization of queue internal memory. It embeds all underlying - * event data and doesn't require any memory allocation while posting and dispatching. - * All of these makes it cannot fail due to memory exhaustion while posting - * - * Usage: - * @code - * #include "mbed.h" - * - * void handler(int data) { ... } - * - * class Device { - * public: - * void handler(int data) { ... } - * }; - * - * Device dev; - * - * // queue with not internal storage for dynamic events - * // accepts only user allocated events - * static EventQueue queue(0); - * // Create events - * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); - * static auto e2 = queue.make_user_allocated_event(handler, 3); - * - * int main() - * { - * e1.call_on(&queue); - * e2.call(); - * - * queue.dispatch(1); - * } - * @endcode - */ -template -class UserAllocatedEvent { -public: - typedef EventQueue::context C; - - /** Create an event - * - * Constructs an event. The specified callback acts as the target - * for the event and is executed in the context of the - * event queue's dispatch loop once posted. - * - * @param f Function to execute when the event is dispatched - * @param args Arguments to bind to the callback - */ - constexpr UserAllocatedEvent(F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(), _post_ref() - { - } - - /** Create an event - * - * Constructs an event. The specified callback acts as the target - * for the event and is executed in the context of the - * event queue's dispatch loop once posted. - * - * @param queue Event queue to dispatch on - * @param f Function to execute when the event is dispatched - * @param args Arguments to bind to the callback - */ - constexpr UserAllocatedEvent(EventQueue *queue, F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(&queue->_equeue), _post_ref() - { - } - - /** Destructor for events - */ -#if !defined(NDEBUG) - // Remove the user provided destructor in release to allow constexpr optimization - // constexpr requires destructor to be trivial and only default one is treated as trivial - ~UserAllocatedEvent() - { - MBED_ASSERT(!_post_ref); - } -#endif - - /** Posts an event onto the underlying event queue, returning void - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - */ - void call() - { - MBED_ASSERT(!_post_ref); - MBED_ASSERT(_equeue); - MBED_UNUSED bool status = post(); - MBED_ASSERT(status); - } - - /** Posts an event onto the event queue passed as argument, returning void - * - * The event is posted to the event queue passed as argument - * and is executed in the context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. - * - */ - void call_on(EventQueue *queue) - { - MBED_ASSERT(!_post_ref); - MBED_UNUSED bool status = post_on(queue); - MBED_ASSERT(status); - } - - /** Posts an event onto the underlying event queue - * - * The event is posted to the event queue passed as argument - * and is executed in the context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * @return False if the event was already posted - * true otherwise - * - */ - bool try_call() - { - return post(); - } - - /** Posts an event onto the event queue passed as argument, - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. - * @return False if the event was already posted - * true otherwise - * - */ - bool try_call_on(EventQueue *queue) - { - return post_on(queue); - } - - /** Posts an event onto the underlying event queue, returning void - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - */ - void operator()() - { - return call(); - } - - /** Configure the delay of an event - * - * @param delay Millisecond delay before dispatching the event - */ - void delay(int delay) - { - MBED_ASSERT(!_post_ref); - _delay = delay; - } - - /** Configure the period of an event - * - * @param period Millisecond period for repeatedly dispatching an event - */ - void period(int period) - { - MBED_ASSERT(!_post_ref); - _period = period; - } - - /** Cancels posted event - * - * Attempts to cancel posted event. It is safe to call - * cancel after an event has already been dispatched. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active, the cancel - * function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. - * - * @return true if event was successfully cancelled - */ - bool cancel() - { - return _post_ref > 0 ? equeue_cancel_user_allocated(_equeue, &_e) : false; - } - - -private: - friend class EventQueue; - struct equeue_event _e; - C _c; - int _delay; - int _period; - struct equeue *_equeue; - uint8_t _post_ref; - - bool post() - { - if (_post_ref) { - return false; - } - core_util_atomic_incr_u8(&_post_ref, 1); - equeue_event_delay(&_e + 1, _delay); - equeue_event_period(&_e + 1, _period); - equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); - return true; - } - - bool post_on(EventQueue *queue) - { - MBED_ASSERT(queue); - if (_post_ref) { - return false; - } - _equeue = &(queue->_equeue); - core_util_atomic_incr_u8(&_post_ref, 1); - equeue_event_delay(&_e + 1, _delay); - equeue_event_period(&_e + 1, _period); - equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); - return true; - } - - static void event_dtor(void *p) - { - UserAllocatedEvent *instance = (UserAllocatedEvent *)(((equeue_event *)p) - 1); - core_util_atomic_decr_u8(&instance->_post_ref, 1); - MBED_ASSERT(!instance->_post_ref); - } - - constexpr static equeue_event get_default_equeue_event() - { - return equeue_event{ 0, 0, 0, NULL, NULL, NULL, 0, -1, &UserAllocatedEvent::event_dtor, NULL }; - } - -public: - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, T *obj, R(T::*method)(ArgTs...), ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } -}; - -// Convenience functions declared here to avoid cyclic -// dependency between Event and EventQueue -template -UserAllocatedEvent EventQueue::make_user_allocated_event(F f, ArgTs... args) -{ - return UserAllocatedEvent(this, f, args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args) -{ - return UserAllocatedEvent(f, args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** @}*/ - -/** @}*/ -} -#endif diff --git a/events/equeue.h b/events/equeue.h deleted file mode 100644 index 5d452fc..0000000 --- a/events/equeue.h +++ /dev/null @@ -1,275 +0,0 @@ - -/* - * Flexible event queue for dispatching events - * - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EQUEUE_H -#define EQUEUE_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Platform specific files -#include "events/internal/equeue_platform.h" - -#include -#include - -/** - * \addtogroup events-public-api - * @{ - */ - -// The minimum size of an event -// This size is guaranteed to fit events created by event_call -#define EQUEUE_EVENT_SIZE (sizeof(struct equeue_event) + 2*sizeof(void*)) - -// Internal event structure -struct equeue_event { - unsigned size; - uint8_t id; - uint8_t generation; - - struct equeue_event *next; - struct equeue_event *sibling; - struct equeue_event **ref; - - unsigned target; - int period; - void (*dtor)(void *); - - void (*cb)(void *); - // data follows -}; - -// Event queue structure -typedef struct equeue { - struct equeue_event *queue; - unsigned tick; - bool break_requested; - uint8_t generation; - - unsigned char *buffer; - unsigned npw2; - void *allocated; - - struct equeue_event *chunks; - struct equeue_slab { - size_t size; - unsigned char *data; - } slab; - - struct equeue_background { - bool active; - void (*update)(void *timer, int ms); - void *timer; - } background; - - equeue_sema_t eventsema; - equeue_mutex_t queuelock; - equeue_mutex_t memlock; -} equeue_t; - - -// Queue lifetime operations -// -// Creates and destroys an event queue. The event queue either allocates a -// buffer of the specified size with malloc or uses a user provided buffer -// if constructed with equeue_create_inplace. -// -// If the event queue creation fails, equeue_create returns a negative, -// platform-specific error code. -// -// If queues are chained, it is needed to unchain them first, before calling destroy, -// or call the destroy function on queues in order that chained queues are destroyed first. -int equeue_create(equeue_t *queue, size_t size); -int equeue_create_inplace(equeue_t *queue, size_t size, void *buffer); -void equeue_destroy(equeue_t *queue); - -// Dispatch events -// -// Executes events until the specified milliseconds have passed. If ms is -// negative, equeue_dispatch will dispatch events indefinitely or until -// equeue_break is called on this queue. -// -// When called with a finite timeout, the equeue_dispatch function is -// guaranteed to terminate. When called with a timeout of 0, the -// equeue_dispatch does not wait and is irq safe. -void equeue_dispatch(equeue_t *queue, int ms); - -// Break out of a running event loop -// -// Forces the specified event queue's dispatch loop to terminate. Pending -// events may finish executing, but no new events will be executed. -void equeue_break(equeue_t *queue); - -// Simple event calls -// -// The specified callback will be executed in the context of the event queue's -// dispatch loop. When the callback is executed depends on the call function. -// -// equeue_call - Immediately post an event to the queue -// equeue_call_in - Post an event after a specified time in milliseconds -// equeue_call_every - Post an event periodically every milliseconds -// -// All equeue_call functions are irq safe and can act as a mechanism for -// moving events out of irq contexts. -// -// The return value is a unique id that represents the posted event and can -// be passed to equeue_cancel. If there is not enough memory to allocate the -// event, equeue_call returns an id of 0. -int equeue_call(equeue_t *queue, void (*cb)(void *), void *data); -int equeue_call_in(equeue_t *queue, int ms, void (*cb)(void *), void *data); -int equeue_call_every(equeue_t *queue, int ms, void (*cb)(void *), void *data); - -// Allocate memory for events -// -// The equeue_alloc function allocates an event that can be manually dispatched -// with equeue_post. The equeue_dealloc function may be used to free an event -// that has not been posted. Once posted, an event's memory is managed by the -// event queue and should not be deallocated. -// -// Both equeue_alloc and equeue_dealloc are irq safe. -// -// The equeue allocator is designed to minimize jitter in interrupt contexts as -// well as avoid memory fragmentation on small devices. The allocator achieves -// both constant-runtime and zero-fragmentation for fixed-size events, however -// grows linearly as the quantity of different sized allocations increases. -// -// The equeue_alloc function returns a pointer to the event's allocated memory -// and acts as a handle to the underlying event. If there is not enough memory -// to allocate the event, equeue_alloc returns null. -void *equeue_alloc(equeue_t *queue, size_t size); -void equeue_dealloc(equeue_t *queue, void *event); - -// Configure an allocated event -// -// equeue_event_delay - Millisecond delay before dispatching an event -// equeue_event_period - Millisecond period for repeating dispatching an event -// equeue_event_dtor - Destructor to run when the event is deallocated -void equeue_event_delay(void *event, int ms); -void equeue_event_period(void *event, int ms); -void equeue_event_dtor(void *event, void (*dtor)(void *)); - -// Post an event onto the event queue -// -// The equeue_post function takes a callback and a pointer to an event -// allocated by equeue_alloc. The specified callback will be executed in the -// context of the event queue's dispatch loop with the allocated event -// as its argument. -// -// The equeue_post function is irq safe and can act as a mechanism for -// moving events out of irq contexts. -// -// The return value is a unique id that represents the posted event and can -// be passed to equeue_cancel. -int equeue_post(equeue_t *queue, void (*cb)(void *), void *event); - -// Post an user allocated event onto the event queue -// -// The equeue_post_user_allocated function takes a callback and a pointer -// to an event allocated by user. The specified callback will be executed -// in the context of the event queue's dispatch loop with the allocated -// event as its argument. -// -// The equeue_post_user_allocated function is irq safe and can act as -// a mechanism for moving events out of irq contexts. -void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event); - -// Cancel an in-flight event -// -// Attempts to cancel an event referenced by the unique id returned from -// equeue_call or equeue_post. It is safe to call equeue_cancel after an event -// has already been dispatched. -// -// The equeue_cancel function is irq safe. -// -// If called while the event queue's dispatch loop is active in another thread, -// equeue_cancel does not guarantee that the event will not execute after it returns as -// the event may have already begun executing. -// Returning true guarantees that cancel succeeded and event will not execute. -// Returning false if invalid id or already started executing. -bool equeue_cancel(equeue_t *queue, int id); - -// Cancel an in-flight user allocated event -// -// Attempts to cancel an event referenced by its address. -// It is safe to call equeue_cancel_user_allocated after an event -// has already been dispatched. -// -// The equeue_cancel_user_allocated function is irq safe. -// -// If called while the event queue's dispatch loop is active, -// equeue_cancel_user_allocated does not guarantee that the event -// will not not execute after it returns as the event may have -// already begun executing. -bool equeue_cancel_user_allocated(equeue_t *queue, void *event); - -// Query how much time is left for delayed event -// -// If event is delayed, this function can be used to query how much time -// is left until the event is due to be dispatched. -// -// This function is irq safe. -// -int equeue_timeleft(equeue_t *q, int id); - -// Query how much time is left for delayed user allocated event -// -// If event is delayed, this function can be used to query how much time -// is left until the event is due to be dispatched. -// -// This function is irq safe. -// -int equeue_timeleft_user_allocated(equeue_t *q, void *event); - -// Background an event queue onto a single-shot timer -// -// 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. -// -// Passing a null update function disables the existing timer. -// -// The equeue_background function allows an event queue to take advantage -// of hardware timers or even other event loops, allowing an event queue to -// be effectively backgrounded. -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 -// use their own buffers and events must be managed independently. -// -// Passing a null queue as the target will unchain the existing queue. -// -// The equeue_chain function allows multiple equeues to be composed, sharing -// the context of a dispatch loop while still being managed independently. -// -// If the event queue chaining fails, equeue_chain returns a negative, -// platform-specific error code. -int equeue_chain(equeue_t *queue, equeue_t *target); - -/** @}*/ - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/events/include/events/Event.h b/events/include/events/Event.h new file mode 100644 index 0000000..8e13c48 --- /dev/null +++ b/events/include/events/Event.h @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EVENT_H +#define EVENT_H + +#include +#include "events/EventQueue.h" +#include "platform/mbed_assert.h" + +namespace events { +/** \defgroup events-public-api Events + * \ingroup mbed-os-public + * @{ + */ + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event; + +/** + * \defgroup events_Event Event class + * @{ + */ + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + using duration = std::chrono::duration; + + /** Create an event + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param q Event queue to dispatch on + * @param f Function to execute when the event is dispatched + */ + template + Event(EventQueue *q, F f) + { + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = duration(0); + _event->period = duration(-1); + + _event->post = &Event::event_post; + _event->dtor = &Event::event_dtor; + + new (_event + 1) F(std::move(f)); + + _event->ref = 1; + } + } + + /** Copy constructor for events + */ + Event(const Event &e) + { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + /** Assignment operator for events + */ + Event &operator=(const Event &that) + { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + /** Destructor for events + */ + ~Event() + { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param d Millisecond delay before dispatching the event + */ + void delay(duration d) + { + if (_event) { + _event->delay = d; + } + } + + /** Configure the delay of an event + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + * + * @param d Millisecond delay before dispatching the event + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + void delay(int d) + { + delay(duration(d)); + } + + /** Configure the period of an event + * + * @param p Millisecond period for repeatedly dispatching an event + */ + void period(duration p) + { + if (_event) { + _event->period = p; + } + } + + /** Configure the period of an event + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + * + * @param p Millisecond period for repeatedly dispatching an event + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + void period(int p) + { + period(duration(p)); + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param args Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(ArgTs... args) const + { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, args...); + return _event->id; + } + + /** Posts an event onto the underlying event queue, returning void + * + * @param args Arguments to pass to the event + */ + void call(ArgTs... args) const + { + MBED_UNUSED int id = post(args...); + MBED_ASSERT(id); + } + + /** Posts an event onto the underlying event queue, returning void + * + * @param args Arguments to pass to the event + */ + void operator()(ArgTs... args) const + { + return call(args...); + } + + /** Static thunk for passing as C-style function + * + * @param func Event to call passed as a void pointer + * @param args Arguments to pass to the event + */ + static void thunk(void *func, ArgTs... args) + { + return static_cast(func)->call(args...); + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() const + { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + duration delay; + duration period; + + int (*post)(struct event *, ArgTs... args); + void (*dtor)(struct event *); + + // F follows + } *_event; + + // Event attributes + template + static int event_post(struct event *e, ArgTs... args) + { + typedef EventQueue::context C; + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*(F *)(e + 1), args...); + equeue_event_delay(p, e->delay.count()); + equeue_event_period(p, e->period.count()); + equeue_event_dtor(p, &EventQueue::function_dtor); + return equeue_post(e->equeue, &EventQueue::function_call, p); + } + + template + static void event_dtor(struct event *e) + { + ((F *)(e + 1))->~F(); + } + +public: + /** Create an event + * @param q Event queue to dispatch on + * @param f Function to execute when the event is dispatched + * @param context_args Arguments to bind to the callback, these arguments are + * allocated on an IRQ-safe allocator from the event queue's + * memory pool. Must be type-compatible with bound_args, the + * arguments to the underlying callback. + */ + template + Event(EventQueue *q, F f, ContextArgTs... context_args) : + Event(q, EventQueue::context(f, context_args...)) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, ArgTs...), B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, ArgTs...) const, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, ArgTs...), B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, ArgTs...) const, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, ArgTs...), B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } +}; + +/** @}*/ + + +// Convenience functions declared here to avoid cyclic +// dependency between Event and EventQueue +template +Event EventQueue::event(R(*func)(ArgTs...)) +{ + return Event(this, func); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(ArgTs...)) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(ArgTs...) const) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(ArgTs...) volatile) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(mbed::Callback cb) +{ + return Event(this, cb); +} + +template +Event EventQueue::event(R(*func)(B0, ArgTs...), C0 c0) +{ + return Event(this, func, c0); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0) +{ + return Event(this, cb, c0); +} + +template +Event EventQueue::event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1) +{ + return Event(this, func, c0, c1); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1) +{ + return Event(this, cb, c0, c1); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) +{ + return Event(this, func, c0, c1, c2); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2) +{ + return Event(this, cb, c0, c1, c2); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, func, c0, c1, c2, c3); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, cb, c0, c1, c2, c3); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, func, c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, cb, c0, c1, c2, c3, c4); +} + +/** @}*/ +} +#endif diff --git a/events/include/events/EventQueue.h b/events/include/events/EventQueue.h new file mode 100644 index 0000000..1bcc018 --- /dev/null +++ b/events/include/events/EventQueue.h @@ -0,0 +1,1512 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include "events/equeue.h" +#include "platform/Callback.h" +#include "platform/NonCopyable.h" +#include +#include +#include +#include + +namespace events { +/** + * \addtogroup events-public-api + * @{ + */ + +/** EVENTS_EVENT_SIZE + * Minimum size of an event + * This size fits a Callback at minimum + */ +#define EVENTS_EVENT_SIZE \ + (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) + +/** EVENTS_QUEUE_SIZE + * Default size of buffer for events + */ +#define EVENTS_QUEUE_SIZE (32*EVENTS_EVENT_SIZE) + +// Predeclared classes +template +class Event; +template +class UserAllocatedEvent; + +/** + * \defgroup events_EventQueue EventQueue class + * @{ + */ + +/** EventQueue + * + * Flexible event queue for dispatching events + */ +class EventQueue : private mbed::NonCopyable { +public: + using duration = std::chrono::duration; + + /** Create an EventQueue + * + * Create an event queue. The event queue either allocates a buffer of + * the specified size with malloc or uses the user provided buffer or + * uses 1B dummy buffer if 0 size passed. + * + * 0 size queue is a special purpose queue to dispatch static events + * only (see UserAllocatedEvent). Such a queue gives the guarantee + * that no dynamic memory allocation will take place while queue + * creation and events posting & dispatching. + * + * @param size Size of buffer to use for events in bytes + * (default to EVENTS_QUEUE_SIZE) + * If 0 provided then 1B dummy buffer is used + * @param buffer Pointer to buffer to use for events + * (default to NULL) + */ + EventQueue(unsigned size = EVENTS_QUEUE_SIZE, unsigned char *buffer = NULL); + + /** Destroy an EventQueue + */ + ~EventQueue(); + + /** Dispatch events + * + * Executes events until the specified milliseconds have passed. + * If ms is negative, the dispatch function will dispatch events + * indefinitely or until break_dispatch is called on this queue. + * + * When called with a finite timeout, the dispatch function is guaranteed + * to terminate. When called with a timeout of 0, the dispatch function + * does not wait and is IRQ safe. + * + * @param ms Time to wait for events in milliseconds, a negative + * value will dispatch events indefinitely + * (default to -1) + */ + void dispatch(int ms = -1); + + /** Dispatch events without a timeout + * + * This is equivalent to EventQueue::dispatch with no arguments, but + * avoids overload ambiguities when passed as a callback. + * + * @see EventQueue::dispatch + */ + void dispatch_forever() + { + dispatch(); + } + + /** Break out of a running event loop + * + * Forces the specified event queue's dispatch loop to terminate. Pending + * events may finish executing, but no new events will be executed. + */ + void break_dispatch(); + + /** Millisecond counter + * + * Returns the underlying tick of the event queue represented as the + * number of milliseconds that have passed since an arbitrary point in + * time. Intentionally overflows to 0 after 2^32-1. + * + * @return The underlying tick of the event queue in milliseconds + */ + unsigned tick(); + + /** Cancel an in-flight event + * + * Attempts to cancel an event referenced by the unique id returned from + * one of the call functions. It is safe to call cancel after an event + * has already been dispatched. + * + * id must be valid i.e. event must have not finished executing. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active in another thread, + * the cancel function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. A call made from + * the same thread as the dispatch loop will always succeed with a valid id. + * + * @param id Unique id of the event + * @return true if event was successfully cancelled + * false if event was not cancelled (invalid id or executing already begun) + */ + bool cancel(int id); + + /** Cancel an in-flight user allocated event + * + * Attempts to cancel an UserAllocatedEvent referenced by its address + * It is safe to call cancel after an event has already been dispatched. + * + * Event must be valid i.e. event must have not finished executing + * and must have been bound to this queue. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active in another thread, + * the cancel function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. A call made from + * the same thread as the dispatch loop will always succeed with a valid id. + * + * @param event Address of the event + * @return true if event was successfully cancelled + * false if event was not cancelled (invalid queue or executing already begun) + */ + template + bool cancel(UserAllocatedEvent *event) + { + if (event->_equeue != &_equeue) { + return false; + } + return equeue_cancel_user_allocated(&_equeue, event); + } + + /** Query how much time is left for delayed event + * + * If the event is delayed, this function can be used to query how much time + * is left until the event is due to be dispatched. + * + * id must be valid i.e. event must have not finished executing. + * + * This function is IRQ safe. + * + * @param id Unique id of the event + * + * @return Remaining time in milliseconds or + * 0 if event is already due to be dispatched or + * is currently executing. + * Undefined if id is invalid. + * + */ + int time_left(int id); + + /** Query how much time is left for delayed UserAllocatedEvent + * + * If the event is delayed, this function can be used to query how much time + * is left until the event is due to be dispatched. + * + * Event must be valid i.e. event must have not finished executing + * and must have been bound to this queue. + * + * This function is IRQ safe. + * + * @param event Address of the event + * + * @return Remaining time in milliseconds or + * 0 if event is already due to be dispatched or + * is currently executing. + * Undefined if id is invalid. + * + */ + template + int time_left(UserAllocatedEvent *event) + { + if (event && event->_equeue != &_equeue) { + return -1; + } + return equeue_timeleft_user_allocated(&_equeue, &event->_e); + } + + /** Background an event queue onto a single-shot timer-interrupt + * + * When updated, the event queue will call the provided update function + * with a timeout indicating when the queue should be dispatched. A + * negative timeout will be passed to the update function when the + * timer-interrupt is no longer needed. + * + * Passing a null function disables the existing update function. + * + * The background function allows an event queue to take advantage of + * hardware timers or other event loops, allowing an event queue to be + * ran in the background without consuming the foreground thread. + * + * @param update Function called to indicate when the queue should be + * dispatched + */ + void background(mbed::Callback update); + + /** Chain an event queue onto another event queue + * + * After chaining a queue to a target, calling dispatch on the target + * queue will also dispatch events from this queue. The queues use + * their own buffers and events must be handled independently. + * + * A null queue as the target will unchain the existing queue. + * + * The chain function allows multiple event queues to be composed, + * sharing the context of a dispatch loop while still being managed + * independently + * + * @param target Queue that will dispatch this queue's events as a + * part of its dispatch loop + * + * @return Zero on success and negative error code value if chaining fails + * + */ + int chain(EventQueue *target); + + + +#if defined(DOXYGEN_ONLY) + /** Calls an event on the queue + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + * Returned id will remain valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks + * queue.call(printf, "called immediately\n"); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call(F f, Args ...args); + + /** Calls an event on the queue + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * Returned ID remains valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(1); + * + * // events are simple callbacks, call object method + * // with provided parameter + * queue.call(&handler_cb, &EventHandler::handler, 2); + * + * // the dispath method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call(T *obj, R (T::*method)(Args ...args), Args ...args); + // *INDENT-ON* + + /** Calls an event on the queue after a specified delay + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks + * queue.call_in(2s, printf, "called in 2 seconds\n"); + * + * // the dispatch methods executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call_in(duration ms, F f, ArgTs ...args); + + /** Calls an event on the queue after a specified delay + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(3); + * + * // events are simple callbacks, call object method in 2 seconds + * // with provided parameter + * queue.call_in(2s, &handler_cb, &EventHandler::handler, 4); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call_in(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); + // *INDENT-ON* + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param ms Period of the event in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks, call every 2 seconds + * queue.call_every(2s, printf, "Calling every 2 seconds\n"); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call_every(duration ms, F f, ArgTs ...args); + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param ms Period of the event in milliseconds + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(5); + * + * // events are simple callbacks, call object method every 2 seconds + * // with provided parameter + * queue.call_every(2s, &handler_cb, &EventHandler::handler, 6); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call_every(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param func Function to execute when the event is dispatched + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * void handler(int c) { + * printf("Param: %d\r\n", c); + * } + * + * int main() + * { + * EventQueue queue; + * + * // Create event with parameter + * Event e = queue.event(handler, 1); + * e(); + * + * // Create event and post parameter later + * Event e2 = queue.event(handler); + * + * // Post the event with paramter 8 + * e.post(8); + * + * // The dispatch method executes events + * queue.dispatch(); + * + * e2.post(2); + * + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + Event event(R (*func)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * class EventHandler { + * int _id; + * + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() + * { + * EventQueue queue; + * + * EventHandler handler_cb(10); + * + * // Create event on the eventqueue with a method callback + * Event e = queue.event(&handler_cb, &EventHandler::handler); + * + * // Post the event with paramter 8 + * e.post(11); + * + * // The dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + Event event(T *obj, R (T::*method)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param cb Callback object + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * void handler(int c) { + * printf("Param: %d\r\n", c); + * } + * + * int main() + * { + * EventQueue queue; + * // Create callback object acting as a function + * // pointer to handler + * Callback cb(handler); + * + * // Pass the callback object to the eventqueue + * Event e = queue.event(cb); + * + * // Post the event with parameter 8 + * e.post(9); + * + * // The dispatch method executes events + * q.dispatch(); + * } + * @endcode + */ + template + Event event(mbed::Callback cb, ContextArgTs ...context_args); + + /** Creates an user allocated event bound to the event queue + * + * Constructs an user allocated event bound to the specified event queue. + * The specified callback acts as the target for the event and is executed + * in the context of the event queue's dispatch loop once posted. + * + * @code + * #include "mbed.h" + * + * void handler(int data) { ... } + * + * class Device { + * public: + * void handler(int data) { ... } + * }; + * + * Device dev; + * + * // queue with not internal storage for dynamic events + * // accepts only user allocated events + * static EventQueue queue(0); + * // Create events + * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); + * static auto e2 = queue.make_user_allocated_event(handler, 3); + * + * int main() + * { + * e1.call_on(&queue); + * e2.call(); + * + * queue.dispatch(1); + * } + * @endcode + * + * @param f Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); + + +#else + + /** Calls an event on the queue + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * Returned ID remains valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * int main() + * { + * EventQueue queue; + * + * Callback cb(handler); + * + * // Create event on the eventqueue with a separate callback object + * Event e = queue.event(cb); + * e.post(1); + * queue.dispatch(); + * } + * @endcode + */ + template + int call(F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + + /** Calls an event on the queue + * @see EventQueue::call + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + int call(F f, ArgTs... args) + { + return call(context(std::move(f), args...)); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + int call_in(duration ms, F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_delay(e, ms.count()); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + int call_in(duration ms, F f, ArgTs... args) + { + return call_in(ms, context(std::move(f), args...)); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, F f) + { + return call_in(duration(ms), std::move(f)); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, F f, ArgTs... args) + { + return call_in(duration(ms), std::move(f), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param ms Period of the event in milliseconds + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + int call_every(duration ms, F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_delay(e, ms.count()); + equeue_event_period(e, ms.count()); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @param ms Period of the event in milliseconds + */ + template + int call_every(duration ms, F f, ArgTs... args) + { + return call_every(ms, context(std::move(f), args...)); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param ms Period of the event in milliseconds + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, F f) + { + return call_every(duration(ms), std::move(f)); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @param ms Period of the event in milliseconds + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, F f, ArgTs... args) + { + return call_every(duration(ms), std::move(f), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param func Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + Event event(R(*func)(ArgTs...)); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(ArgTs...)); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(ArgTs...) const); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(ArgTs...) volatile); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, ArgTs...), C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an user allocated event bound to the event queue + * + * Constructs an user allocated event bound to the specified event queue. + * The specified callback acts as the target for the event and is executed + * in the context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args); +#endif + +protected: +#if !defined(DOXYGEN_ONLY) + template + friend class Event; + template + friend class UserAllocatedEvent; + struct equeue _equeue; + mbed::Callback _update; + + // Function attributes + template + static void function_call(void *p) + { + (*(F *)p)(); + } + + template + static void function_dtor(void *p) + { + ((F *)p)->~F(); + } + + // Context structures + template + struct context; + + template + struct context { + F f; + + constexpr context(F f) + : f(f) {} + + template + void operator()(ArgTs... args) + { + f(args...); + } + }; + + template + struct context { + F f; + C0 c0; + + constexpr context(F f, C0 c0) + : f(f), c0(c0) {} + + template + void operator()(ArgTs... args) + { + f(c0, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + + constexpr context(F f, C0 c0, C1 c1) + : f(f), c0(c0), c1(c1) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + + constexpr context(F f, C0 c0, C1 c1, C2 c2) + : f(f), c0(c0), c1(c1), c2(c2) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + C3 c3; + + constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3) + : f(f), c0(c0), c1(c1), c2(c2), c3(c3) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, c3, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + C3 c3; + C4 c4; + + constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) + : f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, c3, c4, args...); + } + }; +#endif //!defined(DOXYGEN_ONLY) +}; + +/** @}*/ +/** @}*/ + +} + +#endif diff --git a/events/include/events/UserAllocatedEvent.h b/events/include/events/UserAllocatedEvent.h new file mode 100644 index 0000000..94dd360 --- /dev/null +++ b/events/include/events/UserAllocatedEvent.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef USER_ALLOCATED_EVENT_H +#define USER_ALLOCATED_EVENT_H + +#include "events/EventQueue.h" +#include "platform/mbed_assert.h" +#include "platform/mbed_atomic.h" + +namespace events { +/** + * \addtogroup events-public-api + * @{ + */ +template +class UserAllocatedEvent; + +/** + * \defgroup events_Event UserAllocatedEvent class + * @{ + */ + +/** UserAllocatedEvent + * + * Representation of an static event for fine-grain dispatch control. + * + * UserAllocatedEvent provides mechanism for event posting and dispatching + * without utilization of queue internal memory. It embeds all underlying + * event data and doesn't require any memory allocation while posting and dispatching. + * All of these makes it cannot fail due to memory exhaustion while posting + * + * Usage: + * @code + * #include "mbed.h" + * + * void handler(int data) { ... } + * + * class Device { + * public: + * void handler(int data) { ... } + * }; + * + * Device dev; + * + * // queue with not internal storage for dynamic events + * // accepts only user allocated events + * static EventQueue queue(0); + * // Create events + * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); + * static auto e2 = queue.make_user_allocated_event(handler, 3); + * + * int main() + * { + * e1.call_on(&queue); + * e2.call(); + * + * queue.dispatch(1); + * } + * @endcode + */ +template +class UserAllocatedEvent { +public: + typedef EventQueue::context C; + + /** Create an event + * + * Constructs an event. The specified callback acts as the target + * for the event and is executed in the context of the + * event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param args Arguments to bind to the callback + */ + constexpr UserAllocatedEvent(F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(), _post_ref() + { + } + + /** Create an event + * + * Constructs an event. The specified callback acts as the target + * for the event and is executed in the context of the + * event queue's dispatch loop once posted. + * + * @param queue Event queue to dispatch on + * @param f Function to execute when the event is dispatched + * @param args Arguments to bind to the callback + */ + constexpr UserAllocatedEvent(EventQueue *queue, F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(&queue->_equeue), _post_ref() + { + } + + /** Destructor for events + */ +#if !defined(NDEBUG) + // Remove the user provided destructor in release to allow constexpr optimization + // constexpr requires destructor to be trivial and only default one is treated as trivial + ~UserAllocatedEvent() + { + MBED_ASSERT(!_post_ref); + } +#endif + + /** Posts an event onto the underlying event queue, returning void + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + */ + void call() + { + MBED_ASSERT(!_post_ref); + MBED_ASSERT(_equeue); + MBED_UNUSED bool status = post(); + MBED_ASSERT(status); + } + + /** Posts an event onto the event queue passed as argument, returning void + * + * The event is posted to the event queue passed as argument + * and is executed in the context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. + * + */ + void call_on(EventQueue *queue) + { + MBED_ASSERT(!_post_ref); + MBED_UNUSED bool status = post_on(queue); + MBED_ASSERT(status); + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the event queue passed as argument + * and is executed in the context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * @return False if the event was already posted + * true otherwise + * + */ + bool try_call() + { + return post(); + } + + /** Posts an event onto the event queue passed as argument, + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. + * @return False if the event was already posted + * true otherwise + * + */ + bool try_call_on(EventQueue *queue) + { + return post_on(queue); + } + + /** Posts an event onto the underlying event queue, returning void + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + */ + void operator()() + { + return call(); + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) + { + MBED_ASSERT(!_post_ref); + _delay = delay; + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) + { + MBED_ASSERT(!_post_ref); + _period = period; + } + + /** Cancels posted event + * + * Attempts to cancel posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. + * + * @return true if event was successfully cancelled + */ + bool cancel() + { + return _post_ref > 0 ? equeue_cancel_user_allocated(_equeue, &_e) : false; + } + + +private: + friend class EventQueue; + struct equeue_event _e; + C _c; + int _delay; + int _period; + struct equeue *_equeue; + uint8_t _post_ref; + + bool post() + { + if (_post_ref) { + return false; + } + core_util_atomic_incr_u8(&_post_ref, 1); + equeue_event_delay(&_e + 1, _delay); + equeue_event_period(&_e + 1, _period); + equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); + return true; + } + + bool post_on(EventQueue *queue) + { + MBED_ASSERT(queue); + if (_post_ref) { + return false; + } + _equeue = &(queue->_equeue); + core_util_atomic_incr_u8(&_post_ref, 1); + equeue_event_delay(&_e + 1, _delay); + equeue_event_period(&_e + 1, _period); + equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); + return true; + } + + static void event_dtor(void *p) + { + UserAllocatedEvent *instance = (UserAllocatedEvent *)(((equeue_event *)p) - 1); + core_util_atomic_decr_u8(&instance->_post_ref, 1); + MBED_ASSERT(!instance->_post_ref); + } + + constexpr static equeue_event get_default_equeue_event() + { + return equeue_event{ 0, 0, 0, NULL, NULL, NULL, 0, -1, &UserAllocatedEvent::event_dtor, NULL }; + } + +public: + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, T *obj, R(T::*method)(ArgTs...), ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } +}; + +// Convenience functions declared here to avoid cyclic +// dependency between Event and EventQueue +template +UserAllocatedEvent EventQueue::make_user_allocated_event(F f, ArgTs... args) +{ + return UserAllocatedEvent(this, f, args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args) +{ + return UserAllocatedEvent(f, args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** @}*/ + +/** @}*/ +} +#endif diff --git a/events/include/events/equeue.h b/events/include/events/equeue.h new file mode 100644 index 0000000..5d452fc --- /dev/null +++ b/events/include/events/equeue.h @@ -0,0 +1,275 @@ + +/* + * Flexible event queue for dispatching events + * + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EQUEUE_H +#define EQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Platform specific files +#include "events/internal/equeue_platform.h" + +#include +#include + +/** + * \addtogroup events-public-api + * @{ + */ + +// The minimum size of an event +// This size is guaranteed to fit events created by event_call +#define EQUEUE_EVENT_SIZE (sizeof(struct equeue_event) + 2*sizeof(void*)) + +// Internal event structure +struct equeue_event { + unsigned size; + uint8_t id; + uint8_t generation; + + struct equeue_event *next; + struct equeue_event *sibling; + struct equeue_event **ref; + + unsigned target; + int period; + void (*dtor)(void *); + + void (*cb)(void *); + // data follows +}; + +// Event queue structure +typedef struct equeue { + struct equeue_event *queue; + unsigned tick; + bool break_requested; + uint8_t generation; + + unsigned char *buffer; + unsigned npw2; + void *allocated; + + struct equeue_event *chunks; + struct equeue_slab { + size_t size; + unsigned char *data; + } slab; + + struct equeue_background { + bool active; + void (*update)(void *timer, int ms); + void *timer; + } background; + + equeue_sema_t eventsema; + equeue_mutex_t queuelock; + equeue_mutex_t memlock; +} equeue_t; + + +// Queue lifetime operations +// +// Creates and destroys an event queue. The event queue either allocates a +// buffer of the specified size with malloc or uses a user provided buffer +// if constructed with equeue_create_inplace. +// +// If the event queue creation fails, equeue_create returns a negative, +// platform-specific error code. +// +// If queues are chained, it is needed to unchain them first, before calling destroy, +// or call the destroy function on queues in order that chained queues are destroyed first. +int equeue_create(equeue_t *queue, size_t size); +int equeue_create_inplace(equeue_t *queue, size_t size, void *buffer); +void equeue_destroy(equeue_t *queue); + +// Dispatch events +// +// Executes events until the specified milliseconds have passed. If ms is +// negative, equeue_dispatch will dispatch events indefinitely or until +// equeue_break is called on this queue. +// +// When called with a finite timeout, the equeue_dispatch function is +// guaranteed to terminate. When called with a timeout of 0, the +// equeue_dispatch does not wait and is irq safe. +void equeue_dispatch(equeue_t *queue, int ms); + +// Break out of a running event loop +// +// Forces the specified event queue's dispatch loop to terminate. Pending +// events may finish executing, but no new events will be executed. +void equeue_break(equeue_t *queue); + +// Simple event calls +// +// The specified callback will be executed in the context of the event queue's +// dispatch loop. When the callback is executed depends on the call function. +// +// equeue_call - Immediately post an event to the queue +// equeue_call_in - Post an event after a specified time in milliseconds +// equeue_call_every - Post an event periodically every milliseconds +// +// All equeue_call functions are irq safe and can act as a mechanism for +// moving events out of irq contexts. +// +// The return value is a unique id that represents the posted event and can +// be passed to equeue_cancel. If there is not enough memory to allocate the +// event, equeue_call returns an id of 0. +int equeue_call(equeue_t *queue, void (*cb)(void *), void *data); +int equeue_call_in(equeue_t *queue, int ms, void (*cb)(void *), void *data); +int equeue_call_every(equeue_t *queue, int ms, void (*cb)(void *), void *data); + +// Allocate memory for events +// +// The equeue_alloc function allocates an event that can be manually dispatched +// with equeue_post. The equeue_dealloc function may be used to free an event +// that has not been posted. Once posted, an event's memory is managed by the +// event queue and should not be deallocated. +// +// Both equeue_alloc and equeue_dealloc are irq safe. +// +// The equeue allocator is designed to minimize jitter in interrupt contexts as +// well as avoid memory fragmentation on small devices. The allocator achieves +// both constant-runtime and zero-fragmentation for fixed-size events, however +// grows linearly as the quantity of different sized allocations increases. +// +// The equeue_alloc function returns a pointer to the event's allocated memory +// and acts as a handle to the underlying event. If there is not enough memory +// to allocate the event, equeue_alloc returns null. +void *equeue_alloc(equeue_t *queue, size_t size); +void equeue_dealloc(equeue_t *queue, void *event); + +// Configure an allocated event +// +// equeue_event_delay - Millisecond delay before dispatching an event +// equeue_event_period - Millisecond period for repeating dispatching an event +// equeue_event_dtor - Destructor to run when the event is deallocated +void equeue_event_delay(void *event, int ms); +void equeue_event_period(void *event, int ms); +void equeue_event_dtor(void *event, void (*dtor)(void *)); + +// Post an event onto the event queue +// +// The equeue_post function takes a callback and a pointer to an event +// allocated by equeue_alloc. The specified callback will be executed in the +// context of the event queue's dispatch loop with the allocated event +// as its argument. +// +// The equeue_post function is irq safe and can act as a mechanism for +// moving events out of irq contexts. +// +// The return value is a unique id that represents the posted event and can +// be passed to equeue_cancel. +int equeue_post(equeue_t *queue, void (*cb)(void *), void *event); + +// Post an user allocated event onto the event queue +// +// The equeue_post_user_allocated function takes a callback and a pointer +// to an event allocated by user. The specified callback will be executed +// in the context of the event queue's dispatch loop with the allocated +// event as its argument. +// +// The equeue_post_user_allocated function is irq safe and can act as +// a mechanism for moving events out of irq contexts. +void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event); + +// Cancel an in-flight event +// +// Attempts to cancel an event referenced by the unique id returned from +// equeue_call or equeue_post. It is safe to call equeue_cancel after an event +// has already been dispatched. +// +// The equeue_cancel function is irq safe. +// +// If called while the event queue's dispatch loop is active in another thread, +// equeue_cancel does not guarantee that the event will not execute after it returns as +// the event may have already begun executing. +// Returning true guarantees that cancel succeeded and event will not execute. +// Returning false if invalid id or already started executing. +bool equeue_cancel(equeue_t *queue, int id); + +// Cancel an in-flight user allocated event +// +// Attempts to cancel an event referenced by its address. +// It is safe to call equeue_cancel_user_allocated after an event +// has already been dispatched. +// +// The equeue_cancel_user_allocated function is irq safe. +// +// If called while the event queue's dispatch loop is active, +// equeue_cancel_user_allocated does not guarantee that the event +// will not not execute after it returns as the event may have +// already begun executing. +bool equeue_cancel_user_allocated(equeue_t *queue, void *event); + +// Query how much time is left for delayed event +// +// If event is delayed, this function can be used to query how much time +// is left until the event is due to be dispatched. +// +// This function is irq safe. +// +int equeue_timeleft(equeue_t *q, int id); + +// Query how much time is left for delayed user allocated event +// +// If event is delayed, this function can be used to query how much time +// is left until the event is due to be dispatched. +// +// This function is irq safe. +// +int equeue_timeleft_user_allocated(equeue_t *q, void *event); + +// Background an event queue onto a single-shot timer +// +// 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. +// +// Passing a null update function disables the existing timer. +// +// The equeue_background function allows an event queue to take advantage +// of hardware timers or even other event loops, allowing an event queue to +// be effectively backgrounded. +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 +// use their own buffers and events must be managed independently. +// +// Passing a null queue as the target will unchain the existing queue. +// +// The equeue_chain function allows multiple equeues to be composed, sharing +// the context of a dispatch loop while still being managed independently. +// +// If the event queue chaining fails, equeue_chain returns a negative, +// platform-specific error code. +int equeue_chain(equeue_t *queue, equeue_t *target); + +/** @}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/events/include/events/internal/equeue_platform.h b/events/include/events/internal/equeue_platform.h new file mode 100644 index 0000000..5d47c14 --- /dev/null +++ b/events/include/events/internal/equeue_platform.h @@ -0,0 +1,154 @@ +/* + * System specific implementation + * + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EQUEUE_PLATFORM_H +#define EQUEUE_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// Currently supported platforms +// +// Uncomment to select a supported platform or reimplement this file +// for a specific target. +//#define EQUEUE_PLATFORM_POSIX +//#define EQUEUE_PLATFORM_MBED + +// Try to infer a platform if none was manually selected +#if !defined(EQUEUE_PLATFORM_POSIX) \ + && !defined(EQUEUE_PLATFORM_MBED) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__) || defined(__MINGW64__) +#define EQUEUE_PLATFORM_POSIX +#elif defined(__MBED__) +#define EQUEUE_PLATFORM_MBED +#else +#warning "Unknown platform! Please update equeue_platform.h" +#endif +#endif + +// Platform includes +#if defined(EQUEUE_PLATFORM_POSIX) +#include +#elif defined(EQUEUE_PLATFORM_MBED) && defined(MBED_CONF_RTOS_PRESENT) +#include "cmsis_os2.h" +#include "mbed_rtos_storage.h" +#endif + + +// Platform millisecond counter +// +// Return a tick that represents the number of milliseconds that have passed +// since an arbitrary point in time. The granularity does not need to be at +// the millisecond level, however the accuracy of the equeue library is +// limited by the accuracy of this tick. +// +// Must intentionally overflow to 0 after 2^32-1 +void equeue_tick_init(void); +unsigned equeue_tick(void); + + +// Platform mutex type +// +// The equeue library requires at minimum a non-recursive mutex that is +// safe in interrupt contexts. The mutex section is help for a bounded +// amount of time, so simply disabling interrupts is acceptable +// +// If irq safety is not required, a regular blocking mutex can be used. +#if defined(EQUEUE_PLATFORM_POSIX) +typedef pthread_mutex_t equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_WINDOWS) +typedef CRITICAL_SECTION equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_MBED) +typedef unsigned equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_FREERTOS) +typedef UBaseType_t equeue_mutex_t; +#endif + +// Platform mutex operations +// +// The equeue_mutex_create and equeue_mutex_destroy manage the lifetime +// of the mutex. On error, equeue_mutex_create should return a negative +// error code. +// +// The equeue_mutex_lock and equeue_mutex_unlock lock and unlock the +// underlying mutex. +int equeue_mutex_create(equeue_mutex_t *mutex); +void equeue_mutex_destroy(equeue_mutex_t *mutex); +void equeue_mutex_lock(equeue_mutex_t *mutex); +void equeue_mutex_unlock(equeue_mutex_t *mutex); + + +// Platform semaphore type +// +// The equeue library requires a binary semaphore type that can be safely +// signaled from interrupt contexts and from inside a equeue_mutex section. +// +// The equeue_signal_wait is relied upon by the equeue library to sleep the +// processor between events. Spurious wakeups have no negative-effects. +// +// A counting semaphore will also work, however may cause the event queue +// dispatch loop to run unnecessarily. For that matter, equeue_signal_wait +// may even be implemented as a single return statement. +#if defined(EQUEUE_PLATFORM_POSIX) +typedef struct equeue_sema { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool signal; +} equeue_sema_t; +#elif defined(EQUEUE_PLATFORM_MBED) && MBED_CONF_RTOS_API_PRESENT +typedef struct equeue_sema { + // We will actually store a C++ rtos:EventQueue in here; + // attempt to match layout for storage, and assert size in equeue_mbed.cpp +#if MBED_CONF_RTOS_PRESENT + osEventFlagsId_t _id; + mbed_rtos_storage_event_flags_t _obj_mem; +#else + uint32_t _flags; +#endif +} equeue_sema_t; +#elif defined(EQUEUE_PLATFORM_MBED) +typedef int equeue_sema_t; +#endif + +// Platform semaphore operations +// +// The equeue_sema_create and equeue_sema_destroy manage the lifetime +// of the semaphore. On error, equeue_sema_create should return a negative +// error code. +// +// The equeue_sema_signal marks a semaphore as signalled such that the next +// equeue_sema_wait will return true. +// +// The equeue_sema_wait waits for a semaphore to be signalled or returns +// immediately if equeue_sema_signal had been called since the last +// equeue_sema_wait. The equeue_sema_wait returns true if it detected that +// equeue_sema_signal had been called. If ms is negative, equeue_sema_wait +// will wait for a signal indefinitely. +int equeue_sema_create(equeue_sema_t *sema); +void equeue_sema_destroy(equeue_sema_t *sema); +void equeue_sema_signal(equeue_sema_t *sema); +bool equeue_sema_wait(equeue_sema_t *sema, int ms); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/events/include/events/mbed_events.h b/events/include/events/mbed_events.h new file mode 100644 index 0000000..991211c --- /dev/null +++ b/events/include/events/mbed_events.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_EVENTS_H +#define MBED_EVENTS_H + +#ifdef __cplusplus + +#include "events/EventQueue.h" +#include "events/Event.h" +#include "events/UserAllocatedEvent.h" + +#include "events/mbed_shared_queues.h" + +/** \addtogroup events-public-api */ +/** @{*/ +#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE +using namespace events; +#endif +/** @}*/ + +#endif + +#endif diff --git a/events/include/events/mbed_shared_queues.h b/events/include/events/mbed_shared_queues.h new file mode 100644 index 0000000..9be5ff1 --- /dev/null +++ b/events/include/events/mbed_shared_queues.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SHARED_QUEUES_H +#define MBED_SHARED_QUEUES_H + +#include "events/EventQueue.h" + +namespace mbed { +/** \addtogroup events-public-api */ +/** @{*/ + +/** + * Return a pointer to an EventQueue, on which normal tasks can be queued. + * + * All calls to this return the same EventQueue - it and its dispatch thread + * are created on the first call to this function. The dispatch thread + * runs at default priority (currently osPriorityNormal). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the normal event queue should + * normally take less than 10ms to execute, to avoid starving other users. As + * such, users can expect that event latency will typically be 10ms or less, + * but could occasionally be significantly higher if many events are queued. + * + * If an RTOS is not present or the configuration option + * `events.shared-dispatch-from-application` is set to true, then this + * does not create a dedicated dispatch thread - instead the application is + * expected to run the EventQueue's dispatch, for example from main. This is + * necessary for the event loop to work without an RTOS, or an RTOS system can + * save memory by reusing the main stack. + * + * @note + * mbed_event_queue is not itself IRQ safe. To use the mbed_event_queue in + * interrupt context, you must first call `mbed_event_queue()` in threaded + * context and store the pointer for later use. + * + * @return pointer to event queue + */ +events::EventQueue *mbed_event_queue(); + +#ifdef MBED_CONF_RTOS_PRESENT +/** + * Return a pointer to an EventQueue, on which small high-priority tasks can + * be queues, such as simple deferrals from interrupt. + * + * All calls to this return the same EventQueue - it and its thread are + * created on the first call to this function. The dispatch thread + * runs at a high priority (currently osPriorityHigh). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the high-priority event queue + * should normally take less than 100us to execute, to avoid starving other + * users. As such, users can expect that event latency will typically be 100us + * or less, but could occasionally be significantly higher if many events are + * queued. + * + * @note + * mbed_highprio_event_queue is not itself IRQ safe. To use the + * mbed_highprio_event_queue in interrupt context, you must first call + * `mbed_highprio_event_queue()` in threaded context and store the pointer for + * later use. + * + * @return pointer to high-priority event queue + */ + +events::EventQueue *mbed_highprio_event_queue(); + +#endif // MBED_CONF_RTOS_PRESENT + +/** @}*/ + +} + +#endif diff --git a/events/internal/equeue_platform.h b/events/internal/equeue_platform.h deleted file mode 100644 index 5d47c14..0000000 --- a/events/internal/equeue_platform.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * System specific implementation - * - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EQUEUE_PLATFORM_H -#define EQUEUE_PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -// Currently supported platforms -// -// Uncomment to select a supported platform or reimplement this file -// for a specific target. -//#define EQUEUE_PLATFORM_POSIX -//#define EQUEUE_PLATFORM_MBED - -// Try to infer a platform if none was manually selected -#if !defined(EQUEUE_PLATFORM_POSIX) \ - && !defined(EQUEUE_PLATFORM_MBED) -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__) || defined(__MINGW64__) -#define EQUEUE_PLATFORM_POSIX -#elif defined(__MBED__) -#define EQUEUE_PLATFORM_MBED -#else -#warning "Unknown platform! Please update equeue_platform.h" -#endif -#endif - -// Platform includes -#if defined(EQUEUE_PLATFORM_POSIX) -#include -#elif defined(EQUEUE_PLATFORM_MBED) && defined(MBED_CONF_RTOS_PRESENT) -#include "cmsis_os2.h" -#include "mbed_rtos_storage.h" -#endif - - -// Platform millisecond counter -// -// Return a tick that represents the number of milliseconds that have passed -// since an arbitrary point in time. The granularity does not need to be at -// the millisecond level, however the accuracy of the equeue library is -// limited by the accuracy of this tick. -// -// Must intentionally overflow to 0 after 2^32-1 -void equeue_tick_init(void); -unsigned equeue_tick(void); - - -// Platform mutex type -// -// The equeue library requires at minimum a non-recursive mutex that is -// safe in interrupt contexts. The mutex section is help for a bounded -// amount of time, so simply disabling interrupts is acceptable -// -// If irq safety is not required, a regular blocking mutex can be used. -#if defined(EQUEUE_PLATFORM_POSIX) -typedef pthread_mutex_t equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_WINDOWS) -typedef CRITICAL_SECTION equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_MBED) -typedef unsigned equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_FREERTOS) -typedef UBaseType_t equeue_mutex_t; -#endif - -// Platform mutex operations -// -// The equeue_mutex_create and equeue_mutex_destroy manage the lifetime -// of the mutex. On error, equeue_mutex_create should return a negative -// error code. -// -// The equeue_mutex_lock and equeue_mutex_unlock lock and unlock the -// underlying mutex. -int equeue_mutex_create(equeue_mutex_t *mutex); -void equeue_mutex_destroy(equeue_mutex_t *mutex); -void equeue_mutex_lock(equeue_mutex_t *mutex); -void equeue_mutex_unlock(equeue_mutex_t *mutex); - - -// Platform semaphore type -// -// The equeue library requires a binary semaphore type that can be safely -// signaled from interrupt contexts and from inside a equeue_mutex section. -// -// The equeue_signal_wait is relied upon by the equeue library to sleep the -// processor between events. Spurious wakeups have no negative-effects. -// -// A counting semaphore will also work, however may cause the event queue -// dispatch loop to run unnecessarily. For that matter, equeue_signal_wait -// may even be implemented as a single return statement. -#if defined(EQUEUE_PLATFORM_POSIX) -typedef struct equeue_sema { - pthread_mutex_t mutex; - pthread_cond_t cond; - bool signal; -} equeue_sema_t; -#elif defined(EQUEUE_PLATFORM_MBED) && MBED_CONF_RTOS_API_PRESENT -typedef struct equeue_sema { - // We will actually store a C++ rtos:EventQueue in here; - // attempt to match layout for storage, and assert size in equeue_mbed.cpp -#if MBED_CONF_RTOS_PRESENT - osEventFlagsId_t _id; - mbed_rtos_storage_event_flags_t _obj_mem; -#else - uint32_t _flags; -#endif -} equeue_sema_t; -#elif defined(EQUEUE_PLATFORM_MBED) -typedef int equeue_sema_t; -#endif - -// Platform semaphore operations -// -// The equeue_sema_create and equeue_sema_destroy manage the lifetime -// of the semaphore. On error, equeue_sema_create should return a negative -// error code. -// -// The equeue_sema_signal marks a semaphore as signalled such that the next -// equeue_sema_wait will return true. -// -// The equeue_sema_wait waits for a semaphore to be signalled or returns -// immediately if equeue_sema_signal had been called since the last -// equeue_sema_wait. The equeue_sema_wait returns true if it detected that -// equeue_sema_signal had been called. If ms is negative, equeue_sema_wait -// will wait for a signal indefinitely. -int equeue_sema_create(equeue_sema_t *sema); -void equeue_sema_destroy(equeue_sema_t *sema); -void equeue_sema_signal(equeue_sema_t *sema); -bool equeue_sema_wait(equeue_sema_t *sema, int ms); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/events/mbed_events.h b/events/mbed_events.h deleted file mode 100644 index 991211c..0000000 --- a/events/mbed_events.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_EVENTS_H -#define MBED_EVENTS_H - -#ifdef __cplusplus - -#include "events/EventQueue.h" -#include "events/Event.h" -#include "events/UserAllocatedEvent.h" - -#include "events/mbed_shared_queues.h" - -/** \addtogroup events-public-api */ -/** @{*/ -#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE -using namespace events; -#endif -/** @}*/ - -#endif - -#endif diff --git a/events/mbed_shared_queues.h b/events/mbed_shared_queues.h deleted file mode 100644 index 9be5ff1..0000000 --- a/events/mbed_shared_queues.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_SHARED_QUEUES_H -#define MBED_SHARED_QUEUES_H - -#include "events/EventQueue.h" - -namespace mbed { -/** \addtogroup events-public-api */ -/** @{*/ - -/** - * Return a pointer to an EventQueue, on which normal tasks can be queued. - * - * All calls to this return the same EventQueue - it and its dispatch thread - * are created on the first call to this function. The dispatch thread - * runs at default priority (currently osPriorityNormal). - * - * The EventQueue returned may be used to call() Events, or to chain() other - * EventQueues so that they are run in the same context. - * - * Events (or chained EventQueues) executing on the normal event queue should - * normally take less than 10ms to execute, to avoid starving other users. As - * such, users can expect that event latency will typically be 10ms or less, - * but could occasionally be significantly higher if many events are queued. - * - * If an RTOS is not present or the configuration option - * `events.shared-dispatch-from-application` is set to true, then this - * does not create a dedicated dispatch thread - instead the application is - * expected to run the EventQueue's dispatch, for example from main. This is - * necessary for the event loop to work without an RTOS, or an RTOS system can - * save memory by reusing the main stack. - * - * @note - * mbed_event_queue is not itself IRQ safe. To use the mbed_event_queue in - * interrupt context, you must first call `mbed_event_queue()` in threaded - * context and store the pointer for later use. - * - * @return pointer to event queue - */ -events::EventQueue *mbed_event_queue(); - -#ifdef MBED_CONF_RTOS_PRESENT -/** - * Return a pointer to an EventQueue, on which small high-priority tasks can - * be queues, such as simple deferrals from interrupt. - * - * All calls to this return the same EventQueue - it and its thread are - * created on the first call to this function. The dispatch thread - * runs at a high priority (currently osPriorityHigh). - * - * The EventQueue returned may be used to call() Events, or to chain() other - * EventQueues so that they are run in the same context. - * - * Events (or chained EventQueues) executing on the high-priority event queue - * should normally take less than 100us to execute, to avoid starving other - * users. As such, users can expect that event latency will typically be 100us - * or less, but could occasionally be significantly higher if many events are - * queued. - * - * @note - * mbed_highprio_event_queue is not itself IRQ safe. To use the - * mbed_highprio_event_queue in interrupt context, you must first call - * `mbed_highprio_event_queue()` in threaded context and store the pointer for - * later use. - * - * @return pointer to high-priority event queue - */ - -events::EventQueue *mbed_highprio_event_queue(); - -#endif // MBED_CONF_RTOS_PRESENT - -/** @}*/ - -} - -#endif diff --git a/events/source/.mbedignore b/events/source/.mbedignore deleted file mode 100644 index e7e1fb0..0000000 --- a/events/source/.mbedignore +++ /dev/null @@ -1 +0,0 @@ -tests/* diff --git a/events/source/Makefile b/events/source/Makefile deleted file mode 100644 index 6e636bc..0000000 --- a/events/source/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -TARGET = libequeue.a - -CC = gcc -AR = ar -SIZE = size - -SRC += $(wildcard *.c) -OBJ := $(SRC:.c=.o) -DEP := $(SRC:.c=.d) -ASM := $(SRC:.c=.s) - -ifdef DEBUG -CFLAGS += -O0 -g3 -else -CFLAGS += -O2 -endif -ifdef WORD -CFLAGS += -m$(WORD) -endif -CFLAGS += -I. -I.. -I../.. -CFLAGS += -std=c99 -CFLAGS += -Wall -CFLAGS += -D_XOPEN_SOURCE=600 - -LFLAGS += -pthread - - -all: $(TARGET) - -test: tests/tests.o $(OBJ) - $(CC) $(CFLAGS) $^ $(LFLAGS) -o tests/tests - tests/tests - -prof: tests/prof.o $(OBJ) - $(CC) $(CFLAGS) $^ $(LFLAGS) -o tests/prof - tests/prof - -asm: $(ASM) - -size: $(OBJ) - $(SIZE) -t $^ - --include $(DEP) - -%.a: $(OBJ) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) -c -MMD $(CFLAGS) $< -o $@ - -%.s: %.c - $(CC) -S $(CFLAGS) $< -o $@ - -clean: - rm -f $(TARGET) - rm -f tests/tests tests/tests.o tests/tests.d - rm -f tests/prof tests/prof.o tests/prof.d - rm -f $(OBJ) - rm -f $(DEP) - rm -f $(ASM) diff --git a/events/source/tests/prof.c b/events/source/tests/prof.c deleted file mode 100644 index f7b8f90..0000000 --- a/events/source/tests/prof.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Profiling framework for the events library - * - * Copyright (c) 2016 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "events/equeue.h" -#include -#include -#include -#include -#include -#include -#include - - -// Performance measurement utils -#define PROF_RUNS 5 -#define PROF_INTERVAL 100000000 - -#define prof_volatile(t) __attribute__((unused)) volatile t - -typedef uint64_t prof_cycle_t; - -static volatile prof_cycle_t prof_start_cycle; -static volatile prof_cycle_t prof_stop_cycle; -static prof_cycle_t prof_accum_cycle; -static prof_cycle_t prof_baseline_cycle; -static prof_cycle_t prof_iterations; -static const char *prof_units; - -#define prof_cycle() ({ \ - uint32_t a, b; \ - __asm__ volatile ("rdtsc" : "=a" (a), "=d" (b)); \ - ((uint64_t)b << 32) | (uint64_t)a; \ -}) - -#define prof_loop() \ - for (prof_iterations = 0; \ - prof_accum_cycle < PROF_INTERVAL; \ - prof_iterations++) - -#define prof_start() ({ \ - prof_start_cycle = prof_cycle(); \ -}) - -#define prof_stop() ({ \ - prof_stop_cycle = prof_cycle(); \ - prof_accum_cycle += prof_stop_cycle - prof_start_cycle; \ -}) - -#define prof_result(value, units) ({ \ - prof_accum_cycle = value+prof_baseline_cycle; \ - prof_iterations = 1; \ - prof_units = units; \ -}) - -#define prof_measure(func, ...) ({ \ - printf("%s: ...", #func); \ - fflush(stdout); \ - \ - prof_units = "cycles"; \ - prof_cycle_t runs[PROF_RUNS]; \ - for (int i = 0; i < PROF_RUNS; i++) { \ - prof_accum_cycle = 0; \ - prof_iterations = 0; \ - func(__VA_ARGS__); \ - runs[i] = prof_accum_cycle / prof_iterations; \ - } \ - \ - prof_cycle_t res = runs[0]; \ - for (int i = 0; i < PROF_RUNS; i++) { \ - if (runs[i] < res) { \ - res = runs[i]; \ - } \ - } \ - res -= prof_baseline_cycle; \ - printf("\r%s: %"PRIu64" %s", #func, res, prof_units); \ - \ - if (!isatty(0)) { \ - prof_cycle_t prev; \ - while (scanf("%*[^0-9]%"PRIu64, &prev) == 0); \ - int64_t perc = 100*((int64_t)prev - (int64_t)res) / (int64_t)prev; \ - \ - if (perc > 10) { \ - printf(" (\e[32m%+"PRId64"%%\e[0m)", perc); \ - } else if (perc < -10) { \ - printf(" (\e[31m%+"PRId64"%%\e[0m)", perc); \ - } else { \ - printf(" (%+"PRId64"%%)", perc); \ - } \ - } \ - \ - printf("\n"); \ - res; \ -}) - -#define prof_baseline(func, ...) ({ \ - prof_baseline_cycle = 0; \ - prof_baseline_cycle = prof_measure(func, __VA_ARGS__); \ -}) - - -// Various test functions -void no_func(void *eh) -{ -} - - -// Actual performance tests -void baseline_prof(void) -{ - prof_loop() { - prof_start(); - __asm__ volatile(""); - prof_stop(); - } -} - -void equeue_tick_prof(void) -{ - prof_volatile(unsigned) res; - prof_loop() { - prof_start(); - res = equeue_tick(); - prof_stop(); - } -} - -void equeue_alloc_prof(void) -{ - struct equeue q; - equeue_create(&q, 32 * EQUEUE_EVENT_SIZE); - - prof_loop() { - prof_start(); - void *e = equeue_alloc(&q, 8 * sizeof(int)); - prof_stop(); - - equeue_dealloc(&q, e); - } - - equeue_destroy(&q); -} - -void equeue_alloc_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - void *es[count]; - - for (int i = 0; i < count; i++) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = 0; i < count; i++) { - equeue_dealloc(&q, es[i]); - } - - prof_loop() { - prof_start(); - void *e = equeue_alloc(&q, 8 * sizeof(int)); - prof_stop(); - - equeue_dealloc(&q, e); - } - - equeue_destroy(&q); -} - -void equeue_post_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - void *e = equeue_alloc(&q, 0); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - void *e = equeue_alloc(&q, 0); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_future_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - void *e = equeue_alloc(&q, 0); - equeue_event_delay(e, 1000); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_future_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - void *e = equeue_alloc(&q, 0); - equeue_event_delay(e, 1000); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_dispatch_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - equeue_call(&q, no_func, 0); - - prof_start(); - equeue_dispatch(&q, 0); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_dispatch_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - prof_loop() { - for (int i = 0; i < count; i++) { - equeue_call(&q, no_func, 0); - } - - prof_start(); - equeue_dispatch(&q, 0); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_cancel_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - int id = equeue_call(&q, no_func, 0); - - prof_start(); - equeue_cancel(&q, id); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_cancel_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - int id = equeue_call(&q, no_func, 0); - - prof_start(); - equeue_cancel(&q, id); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_alloc_size_prof(void) -{ - size_t size = 32 * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - equeue_alloc(&q, 0); - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - -void equeue_alloc_many_size_prof(int count) -{ - size_t size = count * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - - for (int i = 0; i < count; i++) { - equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - -void equeue_alloc_fragmented_size_prof(int count) -{ - size_t size = count * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - - void *es[count]; - - for (int i = 0; i < count; i++) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = 0; i < count; i++) { - equeue_dealloc(&q, es[i]); - } - - for (int i = count - 1; i >= 0; i--) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = count - 1; i >= 0; i--) { - equeue_dealloc(&q, es[i]); - } - - for (int i = 0; i < count; i++) { - equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - - -// Entry point -int main() -{ - printf("beginning profiling...\n"); - - prof_baseline(baseline_prof); - - prof_measure(equeue_tick_prof); - prof_measure(equeue_alloc_prof); - prof_measure(equeue_post_prof); - prof_measure(equeue_post_future_prof); - prof_measure(equeue_dispatch_prof); - prof_measure(equeue_cancel_prof); - - prof_measure(equeue_alloc_many_prof, 1000); - prof_measure(equeue_post_many_prof, 1000); - prof_measure(equeue_post_future_many_prof, 1000); - prof_measure(equeue_dispatch_many_prof, 100); - prof_measure(equeue_cancel_many_prof, 100); - - prof_measure(equeue_alloc_size_prof); - prof_measure(equeue_alloc_many_size_prof, 1000); - prof_measure(equeue_alloc_fragmented_size_prof, 1000); - - printf("done!\n"); -} diff --git a/events/source/tests/tests.c b/events/source/tests/tests.c deleted file mode 100644 index efbf7f4..0000000 --- a/events/source/tests/tests.c +++ /dev/null @@ -1,902 +0,0 @@ -/* - * Testing framework for the events library - * - * Copyright (c) 2016 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "events/equeue.h" -#include -#include -#include -#include -#include -#include - - -// Testing setup -static jmp_buf test_buf; -static int test_line; -static int test_failure; - -#define test_assert(test) ({ \ - if (!(test)) { \ - test_line = __LINE__; \ - longjmp(test_buf, 1); \ - } \ -}) - -#define test_run(func, ...) ({ \ - printf("%s: ...", #func); \ - fflush(stdout); \ - \ - if (!setjmp(test_buf)) { \ - func(__VA_ARGS__); \ - printf("\r%s: \e[32mpassed\e[0m\n", #func); \ - } else { \ - printf("\r%s: \e[31mfailed\e[0m at line %d\n", #func, test_line); \ - test_failure = true; \ - } \ -}) - - -// Test functions -void pass_func(void *eh) -{ -} - -void simple_func(void *p) -{ - (*(int *)p)++; -} - -void sloth_func(void *p) -{ - usleep(10000); - (*(int *)p)++; -} - -struct indirect { - int *touched; - uint8_t buffer[7]; -}; - -void indirect_func(void *p) -{ - struct indirect *i = (struct indirect *)p; - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -void timing_func(void *p) -{ - struct timing *timing = (struct timing *)p; - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - test_assert(t1 > t2 - 10 && t1 < t2 + 10); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -void fragment_func(void *p) -{ - struct fragment *fragment = (struct fragment *)p; - timing_func(&fragment->timing); - - struct fragment *nfragment = equeue_alloc(fragment->q, fragment->size); - test_assert(nfragment); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - test_assert(id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -void cancel_func(void *p) -{ - struct cancel *cancel = (struct cancel *)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) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_call_in_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int id = equeue_call_in(&q, 10, simple_func, &touched); - test_assert(id); - - equeue_dispatch(&q, 15); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_call_every_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int id = equeue_call_every(&q, 10, simple_func, &touched); - test_assert(id); - - equeue_dispatch(&q, 15); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_post_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched = false; - struct indirect *i = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(i); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - test_assert(id); - - equeue_dispatch(&q, 0); - test_assert(*i->touched); - - equeue_destroy(&q); -} - -// Misc tests -void destructor_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched; - struct indirect *e; - int ids[3]; - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - test_assert(id); - } - - equeue_dispatch(&q, 0); - test_assert(touched == 3); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - test_assert(ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - test_assert(touched == 3); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - test_assert(id); - } - - equeue_destroy(&q); - test_assert(touched == 3); -} - -void allocation_failure_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - void *p = equeue_alloc(&q, 4096); - test_assert(!p); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - test_assert(!p); - - equeue_destroy(&q); -} - -void cancel_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int *ids = malloc(N * sizeof(int)); - - for (int i = 0; i < N; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - } - - for (int i = N - 1; i >= 0; i--) { - test_assert(equeue_cancel(&q, ids[i])); - } - - free(ids); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - equeue_destroy(&q); -} - -void cancel_inflight_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - - int id = equeue_call(&q, simple_func, &touched); - test_assert(equeue_cancel(&q, id)); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - id = equeue_call(&q, simple_func, &touched); - test_assert(equeue_cancel(&q, id)); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - struct cancel *cancel = equeue_alloc(&q, sizeof(struct cancel)); - test_assert(cancel); - cancel->q = &q; - cancel->id = 0; - - id = equeue_post(&q, cancel_func, cancel); - test_assert(id); - - cancel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - equeue_destroy(&q); -} - -void cancel_unnecessarily_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - test_assert(equeue_cancel(&q, id) == (i == 0)); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - test_assert(!equeue_cancel(&q, id)); - } - - bool touched = false; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - test_assert(!equeue_cancel(&q, id)); - } - - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void loop_protect_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(touched); - - touched = false; - equeue_call_every(&q, 1, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void break_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_dispatch(&q, -1); - test_assert(touched); - - equeue_destroy(&q); -} - -void break_no_windup_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int count = 0; - equeue_call_every(&q, 0, simple_func, &count); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, -1); - test_assert(count == 1); - - count = 0; - equeue_dispatch(&q, 55); - test_assert(count > 1); - - equeue_destroy(&q); -} - -void period_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int count = 0; - equeue_call_every(&q, 10, simple_func, &count); - - equeue_dispatch(&q, 55); - test_assert(count == 5); - - 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); - - int touched = 0; - 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); -} - -void background_func(void *p, int ms) -{ - *(unsigned *)p = ms; -} - -void background_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - test_assert(id); - - unsigned ms; - equeue_background(&q, background_func, &ms); - test_assert(ms == 20); - - id = equeue_call_in(&q, 10, pass_func, 0); - test_assert(id); - test_assert(ms == 10); - - id = equeue_call(&q, pass_func, 0); - test_assert(id); - test_assert(ms == 0); - - equeue_dispatch(&q, 0); - test_assert(ms == 10); - - equeue_destroy(&q); - 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); - - test_assert(equeue_cancel(&q1, id1)); - test_assert(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); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -void unchain_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(&q1, simple_func, &touched); - int id2 = equeue_call(&q2, simple_func, &touched); - test_assert(id1 && id2); - - equeue_dispatch(&q1, 0); - test_assert(touched == 2); - - equeue_chain(&q2, 0); - equeue_chain(&q1, &q2); - - id1 = equeue_call(&q1, simple_func, &touched); - id2 = equeue_call(&q2, simple_func, &touched); - test_assert(id1 && id2); - - equeue_dispatch(&q2, 0); - test_assert(touched == 4); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests -void simple_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - test_assert(!err); - - for (int i = 0; i < N; i++) { - struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); - test_assert(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - test_assert(id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -void fragmenting_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, - 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); - test_assert(!err); - - for (int i = 0; i < N; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = equeue_alloc(&q, size); - test_assert(fragment); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - test_assert(id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -struct ethread { - pthread_t thread; - equeue_t *q; - int ms; -}; - -static void *ethread_dispatch(void *p) -{ - struct ethread *t = (struct ethread *)p; - equeue_dispatch(t->q, t->ms); - return 0; -} - -void multithreaded_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - test_assert(!err); - - struct ethread t; - t.q = &q; - t.ms = N * 100; - err = pthread_create(&t.thread, 0, ethread_dispatch, &t); - test_assert(!err); - - for (int i = 0; i < N; i++) { - struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); - test_assert(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - test_assert(id); - } - - err = pthread_join(t.thread, 0); - test_assert(!err); - - equeue_destroy(&q); -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -void simple_breaker(void *p) -{ - struct count_and_queue *caq = (struct count_and_queue *)p; - equeue_break(caq->q); - usleep(10000); - caq->p++; -} - -void break_request_cleared_on_timeout(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_every(&q, 10, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - test_assert(pq.p == 1); - - test_assert(equeue_cancel(&q, id)); - - int count = 0; - equeue_call_every(&q, 10, simple_func, &count); - - equeue_dispatch(&q, 55); - test_assert(count > 1); - - equeue_destroy(&q); -} - -void sibling_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 1024); - test_assert(!err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - test_assert(!s->next); - } - } - test_assert(equeue_cancel(&q, id0)); - test_assert(equeue_cancel(&q, id1)); - test_assert(equeue_cancel(&q, id2)); - equeue_destroy(&q); -} - -struct user_allocated_event { - struct equeue_event e; - bool touched; -}; - -void user_allocated_event_test() -{ - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - test_assert(!err); - - bool touched = false; - struct user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - struct user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - test_assert(0 != equeue_call(&q, simple_func, &touched)); - test_assert(0 == equeue_call(&q, simple_func, &touched)); - test_assert(0 == equeue_call(&q, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - test_assert(equeue_cancel_user_allocated(&q, &e3.e)); - - equeue_dispatch(&q, 1); - - test_assert(true == touched); - test_assert(true == e1.touched); - test_assert(true == e2.touched); - test_assert(false == e3.touched); - test_assert(true == e4.touched); - test_assert(true == e5.touched); - - equeue_dispatch(&q, 10); - - test_assert(true == touched); - test_assert(true == e1.touched); - test_assert(true == e2.touched); - test_assert(false == e3.touched); - test_assert(true == e4.touched); - test_assert(true == e5.touched); - - equeue_destroy(&q); -} - -void id_cycle() -{ - equeue_t q; - int err = equeue_create(&q, 10000000); - test_assert(!err); - - for (int i = 0; i < 300; i++) { - int id = equeue_call(&q, pass_func, 0); - test_assert(id != 0); - test_assert(equeue_cancel(&q, id)); - } - - equeue_destroy(&q); -} - -int main() -{ - printf("beginning tests...\n"); - - test_run(simple_call_test); - test_run(simple_call_in_test); - test_run(simple_call_every_test); - test_run(simple_post_test); - test_run(destructor_test); - test_run(allocation_failure_test); - test_run(cancel_test, 20); - test_run(cancel_inflight_test); - test_run(cancel_unnecessarily_test); - test_run(loop_protect_test); - test_run(break_test); - test_run(break_no_windup_test); - test_run(period_test); - test_run(nested_test); - test_run(sloth_test); - test_run(background_test); - test_run(chain_test); - test_run(unchain_test); - test_run(multithread_test); - test_run(simple_barrage_test, 20); - test_run(fragmenting_barrage_test, 20); - test_run(multithreaded_barrage_test, 20); - test_run(break_request_cleared_on_timeout); - test_run(sibling_test); - test_run(user_allocated_event_test); - test_run(id_cycle); - printf("done!\n"); - return test_failure; -} diff --git a/events/tests/.mbedignore b/events/tests/.mbedignore new file mode 100644 index 0000000..3700faa --- /dev/null +++ b/events/tests/.mbedignore @@ -0,0 +1,2 @@ +UNITTESTS/* +unit/ \ No newline at end of file diff --git a/events/tests/TESTS/events/equeue/main.cpp b/events/tests/TESTS/events/equeue/main.cpp new file mode 100644 index 0000000..96c18d6 --- /dev/null +++ b/events/tests/TESTS/events/equeue/main.cpp @@ -0,0 +1,1093 @@ +/* mbed Microcontroller Library + * Copyright (c) 2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_EXTENDED_TESTS +#error [NOT_SUPPORTED] When running on CI this test is disabled due to limiting testing time. +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include "events/equeue.h" +#include "mbed.h" + +using namespace utest::v1; + +#define TEST_EQUEUE_SIZE (4*EVENTS_EVENT_SIZE) +#define TEST_THREAD_STACK_SIZE 512 +#define DISPATCH_INFINITE -1 + +// Test functions +static void pass_func(void *eh) +{ +} + +static void simple_func(void *p) +{ + uint8_t *d = reinterpret_cast(p); + if (*d < 255) { + (*d)++; + } +} + +static void sloth_func(void *p) +{ + ThisThread::sleep_for(10); + (*(reinterpret_cast(p)))++; +} + +struct indirect { + uint8_t *touched; + uint8_t buffer[7]; +}; + +static void indirect_func(void *p) +{ + struct indirect *i = reinterpret_cast(p); + (*i->touched)++; +} + +struct timing { + unsigned tick; + unsigned delay; +}; + +static void timing_func(void *p) +{ + struct timing *timing = reinterpret_cast(p); + unsigned tick = equeue_tick(); + + unsigned t1 = timing->delay; + unsigned t2 = tick - timing->tick; + TEST_ASSERT_UINT_WITHIN(10, t2, t1); + + timing->tick = tick; +} + +struct fragment { + equeue_t *q; + size_t size; + struct timing timing; +}; + +static void fragment_func(void *p) +{ + struct fragment *fragment = reinterpret_cast(p); + timing_func(&fragment->timing); + + struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); + TEST_ASSERT_NOT_NULL(nfragment); + + *nfragment = *fragment; + equeue_event_delay(nfragment, fragment->timing.delay); + + int id = equeue_post(nfragment->q, fragment_func, nfragment); + TEST_ASSERT_NOT_EQUAL(0, id); +} + +struct cancel { + equeue_t *q; + int id; +}; + +static void cancel_func(void *p) +{ + struct cancel *ccel = reinterpret_cast(p); + equeue_cancel(ccel->q, ccel->id); +} + +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +static void nest_func(void *p) +{ + struct nest *nst = reinterpret_cast(p); + equeue_call(nst->q, nst->cb, nst->data); + + ThisThread::sleep_for(10); +} + +static void multithread_thread(equeue_t *p) +{ + equeue_dispatch(p, DISPATCH_INFINITE); +} + +static void background_func(void *p, int ms) +{ + *(reinterpret_cast(p)) = ms; +} + +struct ethread { + equeue_t *q; + int ms; +}; + +static void ethread_dispatch(void *p) +{ + struct ethread *t = reinterpret_cast(p); + equeue_dispatch(t->q, t->ms); +} + +struct count_and_queue { + int p; + equeue_t *q; +}; + +static void simple_breaker(void *p) +{ + struct count_and_queue *caq = reinterpret_cast(p); + equeue_break(caq->q); + ThisThread::sleep_for(10); + caq->p++; +} + +// Simple call tests + +/** Test that equeue executes function passed by equeue_call. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call is executed properly. + */ +static void test_equeue_simple_call() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_dispatch(&q, 10); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_in. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_in is executed properly. + */ +static void test_equeue_simple_call_in() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int id = equeue_call_in(&q, 10, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 15); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_dispatch(&q, 10); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_every. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_every is executed properly. + */ +static void test_equeue_simple_call_every() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int id = equeue_call_every(&q, 10, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 15); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_post. + * + * Given queue is initialized. + * When the event is posted and after that equeue_dispatch is called. + * Then function passed by equeue_post is executed properly. + */ +static void test_equeue_simple_post() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(i); + + i->touched = &touched; + int id = equeue_post(&q, indirect_func, i); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, *i->touched); + + equeue_destroy(&q); +} + + +// Misc tests + +/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. + * + * Given queue is initialized. + * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. + * Then functions attached to equeue events destructors are executed properly. + */ +static void test_equeue_destructor() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct indirect *e; + int ids[3]; + + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(3, touched); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + ids[i] = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, ids[i]); + } + + for (int i = 0; i < 3; i++) { + equeue_cancel(&q, ids[i]); + } + TEST_ASSERT_EQUAL_UINT8(3, touched); + + equeue_dispatch(&q, 0); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_destroy(&q); + TEST_ASSERT_EQUAL_UINT8(3, touched); +} + +/** Test that equeue_alloc returns 0 when equeue can not be allocated. + * + * Given queue is initialized. + * When equeue_alloc is called and equeue can not be allocated + * Then function equeue_alloc returns NULL. + */ +static void test_equeue_allocation_failure() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); + TEST_ASSERT_NULL(p); + + for (int i = 0; i < 100; i++) { + p = equeue_alloc(&q, 0); + } + TEST_ASSERT_NULL(p); + + equeue_destroy(&q); +} + +/** Test that equeue does not execute evenets that has been canceled. + * + * Given queue is initialized. + * When events are canceled by equeue_cancel. + * Then they are not executed by calling equeue_dispatch. + */ +template +static void test_equeue_cancel() +{ + equeue_t q; + int err = equeue_create(&q, (N * EVENTS_EVENT_SIZE)); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int ids[N]; + + for (int i = 0; i < N; i++) { + ids[i] = equeue_call(&q, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, ids[i]); + } + + for (int i = N - 1; i >= 0; i--) { + equeue_cancel(&q, ids[i]); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT(0, touched); + + equeue_destroy(&q); +} + +/** Test that events can be cancelled by function executed by equeue_dispatch. + * + * Given queue is initialized. + * When event is cancelled by another event while dispatching. + * Then event that was cancelled is not being executed. + */ +static void test_equeue_cancel_inflight() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + + int id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); + TEST_ASSERT_NOT_NULL(ccel); + ccel->q = &q; + ccel->id = 0; + + id = equeue_post(&q, cancel_func, ccel); + TEST_ASSERT_NOT_EQUAL(0, id); + + ccel->id = equeue_call(&q, simple_func, &touched); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + equeue_destroy(&q); +} + +/** Test that unnecessary canceling events would not affect executing other events. + * + * Given queue is initialized. + * When event is unnecessary canceled by equeue_cancel. + * Then other events are properly executed after calling equeue_dispatch. + */ +static void test_equeue_cancel_unnecessarily() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + int id = equeue_call(&q, pass_func, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + id = equeue_call(&q, pass_func, 0); + equeue_dispatch(&q, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. + * + * Given queue is initialized. + * When events have 0 ms period time. + * Then dispatching would not end up in infinite loop. + */ +static void test_equeue_loop_protect() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + + touched1 = 0; + uint8_t touched2 = 0; + equeue_call_every(&q, 1, simple_func, &touched2); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break breaks event queue out of dispatching. + * + * Given queue is initialized. + * When equeue_break is called. + * Then event queue will stop dispatching after finisching current dispatching cycle. + */ +static void test_equeue_break() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + uint8_t touched2 = 0; + equeue_call_every(&q, 5, simple_func, &touched2); + + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break function breaks equeue dispatching only once. + * + * Given queue is initialized. + * When equeue_break is called several times. + * Then equeue is stopped only once. + */ +static void test_equeue_break_no_windup() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_break(&q); + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + touched = 0; + equeue_dispatch(&q, 55); + TEST_ASSERT(touched > 1); + + equeue_destroy(&q); +} + +/** Test that function passed by equeue_call_every is being executed periodically. + * + * Given queue is initialized. + * When function is passed by equeue_call_every with specified period. + * Then event is executed (dispatch time/period) times. + */ +static void test_equeue_period() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + TEST_ASSERT_EQUAL_UINT8(5, touched); + + equeue_destroy(&q); +} + +/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. + * + * Given queue is initialized. + * When nested function is added to enqueue. + * Then it is executed in the next dispatch, or after execution of "mother" function. + */ +static void test_equeue_nested() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + TEST_ASSERT_NOT_NULL(nst); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + int id = equeue_post(&q, nest_func, nst); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 5); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + equeue_dispatch(&q, 1); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + touched = 0; + nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + TEST_ASSERT_NOT_NULL(nst); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + id = equeue_post(&q, nest_func, nst); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 20); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. + * + * Given queue is initialized. + * When sloth function is being called before other functions. + * Then if it is possible all functions start according to predefined schedule correctly. + */ +static void test_equeue_sloth() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + uint8_t touched3 = 0; + int id = equeue_call(&q, sloth_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id); + + id = equeue_call_in(&q, 5, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id); + + id = equeue_call_in(&q, 15, simple_func, &touched3); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 20); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + TEST_ASSERT_EQUAL_UINT8(1, touched3); + + equeue_destroy(&q); +} + +/** Test that equeue can be broken of dispatching from a different thread. + * + * Given queue is initialized. + * When equeue starts dispatching in one thread. + * Then it can be stopped from another thread via equeue_break. + */ +static void test_equeue_multithread() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 1, simple_func, &touched); + + Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); + t1.start(callback(multithread_thread, &q)); + ThisThread::sleep_for(10); + equeue_break(&q); + err = t1.join(); + TEST_ASSERT_EQUAL_INT(0, err); + + TEST_ASSERT(touched > 1); + + equeue_destroy(&q); +} + +/** Test that variable referred via equeue_background shows value in ms to the next event. + * + * Given queue is initialized. + * When variable is referred via equeue_background. + * Then it depicts the time in ms to the next event. + */ +static void test_equeue_background() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + int id = equeue_call_in(&q, 20, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + + int ms; + equeue_background(&q, background_func, &ms); + TEST_ASSERT_EQUAL_INT(20, ms); + + id = equeue_call_in(&q, 10, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + TEST_ASSERT_EQUAL_INT(10, ms); + + id = equeue_call(&q, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + TEST_ASSERT_EQUAL_INT(0, ms); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_INT(10, ms); + + equeue_destroy(&q); + TEST_ASSERT_EQUAL_INT(-1, ms); +} + +/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. + * + * Given queue is initialized. + * When target chained equeue is dispatched. + * Then events from both chained equeues are executed. + */ +static void test_equeue_chain() +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call_in(&q2, 5, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call_in(&q2, 10, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 30); + + TEST_ASSERT_EQUAL_UINT8(3, touched1); + TEST_ASSERT_EQUAL_UINT8(3, touched2); + + equeue_destroy(&q2); + equeue_destroy(&q1); +} + +/** Test that unchaining equeues makes them work on their own. + * + * Given queue is initialized. + * When equeue is unchained. + * Then it can be only dispatched by calling with reference to it. + */ +static void test_equeue_unchain() +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + int id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + int id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_chain(&q2, 0); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_dispatch(&q2, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_chain(&q1, &q2); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q2, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_destroy(&q1); + equeue_destroy(&q2); +} + +// Barrage tests + +/** Test that equeue keeps good time at starting events. + * + * Given queue is initialized. + * When equeue is being dispatched. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_simple_barrage() +{ + equeue_t q; + int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + TEST_ASSERT_EQUAL_INT(0, err); + + for (int i = 0; i < N; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + TEST_ASSERT_NOT_NULL(timing); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, N * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. + * + * Given queue is initialized. + * When equeue is being dispatched and new events are added via already placed in equeue. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_fragmenting_barrage() +{ + equeue_t q; + int err = equeue_create(&q, + 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); + TEST_ASSERT_EQUAL_INT(0, err); + + for (int i = 0; i < N; i++) { + size_t size = sizeof(struct fragment) + i * sizeof(int); + struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); + TEST_ASSERT_NOT_NULL(fragment); + + fragment->q = &q; + fragment->size = size; + fragment->timing.tick = equeue_tick(); + fragment->timing.delay = (i + 1) * 100; + equeue_event_delay(fragment, fragment->timing.delay); + + int id = equeue_post(&q, fragment_func, fragment); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, N * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events even if it is working on different thread. + * + * Given queue is initialized. + * When equeue is being dispatched on different thread. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_multithreaded_barrage() +{ + equeue_t q; + int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + TEST_ASSERT_EQUAL_INT(0, err); + + struct ethread t; + t.q = &q; + t.ms = N * 100; + + Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); + + t1.start(callback(ethread_dispatch, &t)); + + for (int i = 0; i < N; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + TEST_ASSERT_NOT_NULL(timing); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + err = t1.join(); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_destroy(&q); +} + +/** Test that break request flag is cleared when equeue stops dispatching timeouts. + * + * Given queue is initialized. + * When equeue break request flag is called but equeue stops dispatching because of timeout. + * Then next equeue dispatch is not stopped. + */ +static void test_equeue_break_request_cleared_on_timeout() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL(0, err); + + struct count_and_queue pq; + pq.p = 0; + pq.q = &q; + + int id = equeue_call_in(&q, 1, simple_breaker, &pq); + + equeue_dispatch(&q, 10); + + TEST_ASSERT_EQUAL_INT(1, pq.p); + + equeue_cancel(&q, id); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + TEST_ASSERT_EQUAL_UINT8(5, touched); + + equeue_destroy(&q); +} + +/** Test that siblings events don't have next pointers. + * + * Given queue is initialized. + * When events are scheduled on the same time. + * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. + */ +static void test_equeue_sibling() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL(0, err); + + int id0 = equeue_call_in(&q, 1, pass_func, 0); + int id1 = equeue_call_in(&q, 1, pass_func, 0); + int id2 = equeue_call_in(&q, 1, pass_func, 0); + + struct equeue_event *e = q.queue; + + for (; e; e = e->next) { + for (struct equeue_event *s = e->sibling; s; s = s->sibling) { + TEST_ASSERT_NULL(s->next); + } + } + equeue_cancel(&q, id0); + equeue_cancel(&q, id1); + equeue_cancel(&q, id2); + equeue_destroy(&q); +} + +struct user_allocated_event { + struct equeue_event e; + uint8_t touched; +}; + +/** Test that equeue executes user allocated events passed by equeue_post. + * + * Given queue is initialized and its size is set to store one event at max in its internal memory. + * When post events allocated in queues internal memory (what is done by calling equeue_call). + * Then only one event can be posted due to queue memory size. + * When post user allocated events. + * Then number of posted events is not limited by queue memory size. + * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. + * Then both types of events are executed properly. + */ +static void test_equeue_user_allocated_event_post() +{ + equeue_t q; + int err = equeue_create(&q, EQUEUE_EVENT_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + + TEST_ASSERT_NOT_EQUAL(0, equeue_call_every(&q, 10, simple_func, &touched)); + TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); + TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); + + equeue_post_user_allocated(&q, simple_func, &e1.e); + equeue_post_user_allocated(&q, simple_func, &e2.e); + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_post_user_allocated(&q, simple_func, &e4.e); + equeue_post_user_allocated(&q, simple_func, &e5.e); + equeue_cancel_user_allocated(&q, &e3.e); + equeue_cancel_user_allocated(&q, &e3.e); + + equeue_dispatch(&q, 11); + + TEST_ASSERT_EQUAL_UINT8(1, touched); + TEST_ASSERT_EQUAL_UINT8(1, e1.touched); + TEST_ASSERT_EQUAL_UINT8(1, e2.touched); + TEST_ASSERT_EQUAL_UINT8(0, e3.touched); + TEST_ASSERT_EQUAL_UINT8(1, e4.touched); + TEST_ASSERT_EQUAL_UINT8(1, e5.touched); + + e3.e.target = 10; // set target as it's modified by equeue_call + e3.e.period = 10; // set period as it's reset by equeue_cancel + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_dispatch(&q, 101); + + TEST_ASSERT_EQUAL_UINT8(11, touched); + TEST_ASSERT_EQUAL_UINT8(1, e1.touched); + TEST_ASSERT_EQUAL_UINT8(11, e2.touched); + TEST_ASSERT_EQUAL_UINT8(10, e3.touched); + TEST_ASSERT_EQUAL_UINT8(11, e4.touched); + TEST_ASSERT_EQUAL_UINT8(1, e5.touched); + + equeue_destroy(&q); +} + +Case cases[] = { + Case("simple call test", test_equeue_simple_call), + Case("simple call in test", test_equeue_simple_call_in), + Case("simple call every test", test_equeue_simple_call_every), + Case("simple post test", test_equeue_simple_post), + + Case("destructor test", test_equeue_destructor), + Case("allocation failure test", test_equeue_allocation_failure), + Case("cancel test", test_equeue_cancel<20>), + Case("cancel inflight test", test_equeue_cancel_inflight), + Case("cancel unnecessarily test", test_equeue_cancel_unnecessarily), + Case("loop protect test", test_equeue_loop_protect), + Case("break test", test_equeue_break), + Case("break no windup test", test_equeue_break_no_windup), + Case("period test", test_equeue_period), + Case("nested test", test_equeue_nested), + Case("sloth test", test_equeue_sloth), + + Case("multithread test", test_equeue_multithread), + + Case("background test", test_equeue_background), + Case("chain test", test_equeue_chain), + Case("unchain test", test_equeue_unchain), + + Case("simple barrage test", test_equeue_simple_barrage<20>), + Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>), + Case("multithreaded barrage test", test_equeue_multithreaded_barrage<10>), + Case("break request cleared on timeout test", test_equeue_break_request_cleared_on_timeout), + Case("sibling test", test_equeue_sibling), + Case("user allocated event test", test_equeue_user_allocated_event_post) + +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(40, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // MBED_EXTENDED_TESTS diff --git a/events/tests/TESTS/events/queue/main.cpp b/events/tests/TESTS/events/queue/main.cpp new file mode 100644 index 0000000..1b03793 --- /dev/null +++ b/events/tests/TESTS/events/queue/main.cpp @@ -0,0 +1,549 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_events.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported +#else + +using namespace utest::v1; + +// Assume that tolerance is 5% of measured time. +#define DELTA(ms) (ms / 20) + +// TEST_EQUEUE_SIZE was reduced below 1024B to fit this test to devices with small RAM (RAM <= 16kB) +// additionally TEST_EQUEUE_SIZE was expressed in EVENTS_EVENT_SIZE to increase readability +// (for more details about EVENTS_EVENT_SIZE see EventQueue constructor) +#define TEST_EQUEUE_SIZE (18*EVENTS_EVENT_SIZE) + +// By empirical, we take 80MHz CPU/2ms delay as base tolerance for time left test. +// For higher CPU frequency, tolerance is fixed to 2ms. +// For lower CPU frequency, tolerance is inversely proportional to CPU frequency. +// E.g.: +// 100MHz: 2ms +// 80MHz: 2ms +// 64MHz: 3ms +// 48MHz: 4ms +#define ALLOWED_TIME_LEFT_TOLERANCE_MS ((SystemCoreClock >= 80000000) ? 2 : ((80000000 * 2 + SystemCoreClock - 1) / SystemCoreClock)) + +// flag for called +volatile bool touched = false; + +// static functions +void func5(int a0, int a1, int a2, int a3, int a4) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3 | a4, 0x1f); +} + +void func4(int a0, int a1, int a2, int a3) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3, 0xf); +} + +void func3(int a0, int a1, int a2) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2, 0x7); +} + +void func2(int a0, int a1) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1, 0x3); +} + +void func1(int a0) +{ + touched = true; + TEST_ASSERT_EQUAL(a0, 0x1); +} + +void func0() +{ + touched = true; +} + +#define SIMPLE_POSTS_TEST(i, ...) \ +void simple_posts_test##i() { \ + EventQueue queue(TEST_EQUEUE_SIZE); \ + \ + touched = false; \ + queue.call(func##i,##__VA_ARGS__); \ + queue.dispatch(0); \ + TEST_ASSERT(touched); \ + \ + touched = false; \ + queue.call_in(1, func##i,##__VA_ARGS__); \ + queue.dispatch(2); \ + TEST_ASSERT(touched); \ + \ + touched = false; \ + queue.call_every(1, func##i,##__VA_ARGS__); \ + queue.dispatch(2); \ + TEST_ASSERT(touched); \ +} + +SIMPLE_POSTS_TEST(5, 0x01, 0x02, 0x04, 0x08, 0x010) +SIMPLE_POSTS_TEST(4, 0x01, 0x02, 0x04, 0x08) +SIMPLE_POSTS_TEST(3, 0x01, 0x02, 0x04) +SIMPLE_POSTS_TEST(2, 0x01, 0x02) +SIMPLE_POSTS_TEST(1, 0x01) +SIMPLE_POSTS_TEST(0) + + +void time_func(Timer *t, int ms) +{ + TEST_ASSERT_INT_WITHIN(DELTA(ms), ms, t->read_ms()); + t->reset(); +} + +template +void call_in_test() +{ + Timer tickers[N]; + + EventQueue queue(TEST_EQUEUE_SIZE); + + for (int i = 0; i < N; i++) { + tickers[i].start(); + queue.call_in((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); + } + + queue.dispatch(N * 100); +} + +template +void call_every_test() +{ + Timer tickers[N]; + + EventQueue queue(TEST_EQUEUE_SIZE); + + for (int i = 0; i < N; i++) { + tickers[i].start(); + queue.call_every((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); + } + + queue.dispatch(N * 100); +} + +void allocate_failure_test() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + int id; + + for (int i = 0; i < 100; i++) { + id = queue.call((void (*)())0); + } + + TEST_ASSERT(!id); +} + +void no() +{ + TEST_ASSERT(false); +} + +template +void cancel_test1() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + + int ids[N]; + + for (int i = 0; i < N; i++) { + ids[i] = queue.call_in(1000, no); + } + + for (int i = N - 1; i >= 0; i--) { + queue.cancel(ids[i]); + } + + queue.dispatch(0); +} + + +// Testing the dynamic arguments to the event class +unsigned counter = 0; + +void count5(unsigned a0, unsigned a1, unsigned a2, unsigned a3, unsigned a5) +{ + counter += a0 + a1 + a2 + a3 + a5; +} + +void count4(unsigned a0, unsigned a1, unsigned a2, unsigned a3) +{ + counter += a0 + a1 + a2 + a3; +} + +void count3(unsigned a0, unsigned a1, unsigned a2) +{ + counter += a0 + a1 + a2; +} + +void count2(unsigned a0, unsigned a1) +{ + counter += a0 + a1; +} + +void count1(unsigned a0) +{ + counter += a0; +} + +void count0() +{ + counter += 0; +} + +void event_class_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + Event e5(&queue, count5); + Event e4(&queue, count5, 1); + Event e3(&queue, count5, 1, 1); + Event e2(&queue, count5, 1, 1, 1); + Event e1(&queue, count5, 1, 1, 1, 1); + Event e0(&queue, count5, 1, 1, 1, 1, 1); + + e5.post(1, 1, 1, 1, 1); + e4.post(1, 1, 1, 1); + e3.post(1, 1, 1); + e2.post(1, 1); + e1.post(1); + e0.post(); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 30); +} + +void event_class_helper_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + Event e5 = queue.event(count5, 1, 1, 1, 1, 1); + Event e4 = queue.event(count4, 1, 1, 1, 1); + Event e3 = queue.event(count3, 1, 1, 1); + Event e2 = queue.event(count2, 1, 1); + Event e1 = queue.event(count1, 1); + Event e0 = queue.event(count0); + + e5.post(); + e4.post(); + e3.post(); + e2.post(); + e1.post(); + e0.post(); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 15); +} + +void event_inference_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + queue.event(count5, 1, 1, 1, 1, 1).post(); + queue.event(count5, 1, 1, 1, 1).post(1); + queue.event(count5, 1, 1, 1).post(1, 1); + queue.event(count5, 1, 1).post(1, 1, 1); + queue.event(count5, 1).post(1, 1, 1, 1); + queue.event(count5).post(1, 1, 1, 1, 1); + + queue.event(callback(count5), 1, 1, 1, 1, 1).post(); + queue.event(callback(count5), 1, 1, 1, 1).post(1); + queue.event(callback(count5), 1, 1, 1).post(1, 1); + queue.event(callback(count5), 1, 1).post(1, 1, 1); + queue.event(callback(count5), 1).post(1, 1, 1, 1); + queue.event(callback(count5)).post(1, 1, 1, 1, 1); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 60); +} + +int timeleft_events[2]; + +void check_time_left(EventQueue *queue, int index, int expected) +{ + const int event_id = timeleft_events[index]; + TEST_ASSERT_INT_WITHIN(ALLOWED_TIME_LEFT_TOLERANCE_MS, expected, queue->time_left(event_id)); + touched = true; +} + +void time_left(EventQueue *queue, int index) +{ + const int event_id = timeleft_events[index]; + TEST_ASSERT_EQUAL(0, queue->time_left(event_id)); +} + +void time_left_test() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + + // Enque check events + TEST_ASSERT(queue.call_in(50, check_time_left, &queue, 0, 100 - 50)); + TEST_ASSERT(queue.call_in(200, check_time_left, &queue, 1, 200 - 200)); + + // Enque events to be checked + timeleft_events[0] = queue.call_in(100, time_left, &queue, 0); + timeleft_events[1] = queue.call_in(200, time_left, &queue, 1); + TEST_ASSERT(timeleft_events[0]); + TEST_ASSERT(timeleft_events[1]); + + queue.dispatch(300); + + // Ensure check was called + TEST_ASSERT(touched); + touched = false; + + int id = queue.call(func0); + TEST_ASSERT(id); + TEST_ASSERT_EQUAL(0, queue.time_left(id)); + queue.dispatch(10); + + // Test invalid event id + TEST_ASSERT_EQUAL(-1, queue.time_left(0)); +} + +void f5(int a1, int a2, int a3, int a4, int a5) +{ + touched = true; +} + +class EventTest { +public: + EventTest() : counter() {} + void f0() + { + counter++; + } + void f1(int a) + { + counter += a; + } + void f5(int a, int b, int c, int d, int e) + { + counter += a + b + c + d + e; + } + uint32_t counter; +}; + +/** Test that queue executes both dynamic and user allocated events. + * + * Given queue is initialized and its size is set to store three Event at max in its internal memory. + * When post queue allocated event. + * Then only three event can be posted due to queue memory size. + * When post user allocated evens. + * Then number of posted events is not limited by queue memory size. + * When both Event and UserAllocatedEvent are posted and queue dispatch is called. + * Then both types of events are executed properly. + * + */ +void mixed_dynamic_static_events_queue_test() +{ + { + EventQueue queue(9 * EVENTS_EVENT_SIZE); + + EventTest e1_test; + Event e1 = queue.event(&e1_test, &EventTest::f0); + e1.delay(10); + e1.period(10); + int id1 = e1.post(); + TEST_ASSERT_NOT_EQUAL(0, id1); + EventTest e2_test; + Event e2 = queue.event(&e2_test, &EventTest::f1, 3); + e2.period(10); + int id2 = e2.post(); + TEST_ASSERT_NOT_EQUAL(0, id2); + EventTest e3_test; + Event e3 = queue.event(&e3_test, &EventTest::f5, 1, 2, 3, 4, 5); + e3.period(10); + int id3 = e3.post(); + TEST_ASSERT_NOT_EQUAL(0, id3); + + + auto ue0 = make_user_allocated_event(func0); + EventTest ue1_test; + auto ue1 = make_user_allocated_event(&ue1_test, &EventTest::f0); + EventTest ue2_test; + auto ue2 = make_user_allocated_event(&ue2_test, &EventTest::f1, 3); + EventTest ue3_test; + auto ue3 = make_user_allocated_event(&ue3_test, &EventTest::f5, 1, 2, 3, 4, 5); + EventTest ue4_test; + auto ue4 = make_user_allocated_event(&ue4_test, &EventTest::f5, 1, 2, 3, 4, 5); + + touched = false; + + ue0.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue0.try_call()); + ue1.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue1.try_call()); + ue2.period(10); + ue2.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue2.try_call()); + ue3.period(10); + ue3.delay(50); + ue3.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue3.try_call()); + ue4.call_on(&queue); + ue4.cancel(); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + ue4.cancel(); + e2.cancel(); + + queue.dispatch(101); + + TEST_ASSERT_EQUAL(true, touched); + TEST_ASSERT_EQUAL(1, ue1_test.counter); + TEST_ASSERT_EQUAL(33, ue2_test.counter); + TEST_ASSERT_EQUAL(90, ue3_test.counter); + TEST_ASSERT_EQUAL(0, ue4_test.counter); + TEST_ASSERT_EQUAL(10, e1_test.counter); + TEST_ASSERT_EQUAL(0, e2_test.counter); + TEST_ASSERT_EQUAL(165, e3_test.counter); + + // user allocated event have to be canceled(removed from the queue) before destruction + // cancel all periodic user events + ue2.cancel(); + ue3.cancel(); + } +} + + +static EventQueue g_queue(0); +static auto ue0 = g_queue.make_user_allocated_event(func0); +static EventTest test1; +static auto ue1 = make_user_allocated_event(&test1, &EventTest::f0); +static EventTest test2; +static auto ue2 = g_queue.make_user_allocated_event(&test2, &EventTest::f1, 3); +static EventTest test3; +static auto ue3 = make_user_allocated_event(&test3, &EventTest::f5, 1, 2, 3, 4, 5); +static EventTest test4; +static auto ue4 = g_queue.make_user_allocated_event(&test4, &EventTest::f5, 1, 2, 3, 4, 5); + + +/** Test that static queue executes user allocated events. + * + * Given static queue is initialized + * When post user allocated evens. + * Then UserAllocatedEvent are posted and dispatched without any error. + */ +void static_events_queue_test() +{ + // check that no dynamic event can be posted + Event e0 = g_queue.event(func0); + TEST_ASSERT_EQUAL(0, e0.post()); + + ue0.delay(100); + ue0.period(200); + + ue1.delay(100); + ue1.period(200); + + ue2.delay(100); + ue2.period(200); + + ue3.delay(100); + ue3.period(200); + + ue4.delay(100); + ue4.period(200); + + ue0.call(); + TEST_ASSERT_EQUAL(false, ue0.try_call()); + ue1.call_on(&g_queue); + TEST_ASSERT_EQUAL(false, ue1.try_call()); + ue2(); + TEST_ASSERT_EQUAL(false, ue2.try_call()); + ue3.call_on(&g_queue); + TEST_ASSERT_EQUAL(false, ue3.try_call()); + ue4.call(); + ue4.cancel(); + ue4.cancel(); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + g_queue.cancel(&ue4); + g_queue.cancel(&ue4); + + g_queue.dispatch(400); + + TEST_ASSERT_EQUAL(2, test1.counter); + TEST_ASSERT_EQUAL(6, test2.counter); + TEST_ASSERT_EQUAL(30, test3.counter); + TEST_ASSERT_EQUAL(0, test4.counter); + + ue4.delay(1); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + g_queue.dispatch(1); + + TEST_ASSERT_EQUAL(2, test1.counter); + TEST_ASSERT_EQUAL(6, test2.counter); + TEST_ASSERT_EQUAL(30, test3.counter); + TEST_ASSERT_EQUAL(15, test4.counter); +} + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(20, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +const Case cases[] = { + Case("Testing calls with 5 args", simple_posts_test5), + Case("Testing calls with 4 args", simple_posts_test4), + Case("Testing calls with 3 args", simple_posts_test3), + Case("Testing calls with 2 args", simple_posts_test2), + Case("Testing calls with 1 args", simple_posts_test1), + Case("Testing calls with 0 args", simple_posts_test0), + + Case("Testing call_in", call_in_test<20>), + Case("Testing call_every", call_every_test<20>), + + Case("Testing allocate failure", allocate_failure_test), + + Case("Testing event cancel 1", cancel_test1<20>), + Case("Testing the event class", event_class_test), + Case("Testing the event class helpers", event_class_helper_test), + Case("Testing the event inference", event_inference_test), + + Case("Testing time_left", time_left_test), + Case("Testing mixed dynamic & static events queue", mixed_dynamic_static_events_queue_test), + Case("Testing static events queue", static_events_queue_test) + +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/events/tests/TESTS/events/timing/main.cpp b/events/tests/TESTS/events/timing/main.cpp new file mode 100644 index 0000000..fb0b759 --- /dev/null +++ b/events/tests/TESTS/events/timing/main.cpp @@ -0,0 +1,151 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_events.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include +#include + +using namespace utest::v1; + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported +#else + +// Test delay +#ifndef TEST_EVENTS_TIMING_TIME +#define TEST_EVENTS_TIMING_TIME 20000 +#endif + +#ifndef TEST_EVENTS_TIMING_MEAN +#define TEST_EVENTS_TIMING_MEAN 25 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 +#endif + +// Random number generation to skew timing values +float gauss(float mu, float sigma) +{ + float x = (float)rand() / ((float)RAND_MAX + 1); + float y = (float)rand() / ((float)RAND_MAX + 1); + float x2pi = x * 2.0 * M_PI; + float g2rad = sqrt(-2.0 * log(1.0 - y)); + float z = cos(x2pi) * g2rad; + return mu + z * sigma; +} + +float chisq(float sigma) +{ + return pow(gauss(0, sqrt(sigma)), 2); +} + + +Timer timer; +DigitalOut led(LED1); + +equeue_sema_t sema; + +// Timer timing test +void timer_timing_test() +{ + timer.reset(); + timer.start(); + int prev = timer.read_us(); + + while (prev < TEST_EVENTS_TIMING_TIME * 1000) { + int next = timer.read_us(); + if (next < prev) { + printf("backwards drift %d -> %d (%08x -> %08x)\r\n", + prev, next, prev, next); + } + TEST_ASSERT(next >= prev); + prev = next; + } +} + +// equeue tick timing test +void tick_timing_test() +{ + unsigned start = equeue_tick(); + int prev = 0; + + while (prev < TEST_EVENTS_TIMING_TIME) { + int next = equeue_tick() - start; + if (next < prev) { + printf("backwards drift %d -> %d (%08x -> %08x)\r\n", + prev, next, prev, next); + } + TEST_ASSERT(next >= prev); + prev = next; + } +} + +// equeue semaphore timing test +void semaphore_timing_test() +{ + srand(0); + timer.reset(); + timer.start(); + + int err = equeue_sema_create(&sema); + TEST_ASSERT_EQUAL(0, err); + + while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) { + int delay = chisq(TEST_EVENTS_TIMING_MEAN); + + int start = timer.read_us(); + equeue_sema_wait(&sema, delay); + int taken = timer.read_us() - start; + + if (taken < (delay * 1000 - 5000) || taken > (delay * 1000 + 5000)) { + printf("delay %dms => error %dus\r\n", delay, abs(1000 * delay - taken)); + } + + TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000); + + led = !led; + } + + equeue_sema_destroy(&sema); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP((number_of_cases + 1)*TEST_EVENTS_TIMING_TIME / 1000, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +const Case cases[] = { + Case("Testing accuracy of timer", timer_timing_test), + Case("Testing accuracy of equeue tick", tick_timing_test), + Case("Testing accuracy of equeue semaphore", semaphore_timing_test), +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/events/tests/UNITTESTS/equeue/test_equeue.cpp b/events/tests/UNITTESTS/equeue/test_equeue.cpp new file mode 100644 index 0000000..ae017cb --- /dev/null +++ b/events/tests/UNITTESTS/equeue/test_equeue.cpp @@ -0,0 +1,1114 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gtest/gtest.h" +#include "events/equeue.h" +#include "mbed.h" +#include +#include + +#define EVENTS_EVENT_SIZE (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) +#define TEST_EQUEUE_SIZE 2048 +#define TEST_THREAD_STACK_SIZE 512 +#define DISPATCH_INFINITE -1 +#define ITERATION_TIMES 10 + +extern unsigned int equeue_global_time; + +class TestEqueue : public testing::Test { + virtual void SetUp() + { + } + + virtual void TearDown() + { + } +}; + +// Test functions +static void pass_func(void *eh) +{ +} + +static void simple_func(void *p) +{ + uint8_t *d = reinterpret_cast(p); + if (*d < 255) { + (*d)++; + } +} + +static void sloth_func(void *p) +{ + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; + (*(reinterpret_cast(p)))++; +} + +struct indirect { + uint8_t *touched; + uint8_t buffer[7]; +}; + +static void indirect_func(void *p) +{ + struct indirect *i = reinterpret_cast(p); + (*i->touched)++; +} + +struct timing { + unsigned tick; + unsigned delay; +}; + +static void timing_func(void *p) +{ + struct timing *timing = reinterpret_cast(p); + unsigned tick = equeue_tick(); + + unsigned t1 = timing->delay; + unsigned t2 = tick - timing->tick; + EXPECT_TRUE(t2 - 10 < t1 < t2 + 10); + + timing->tick = tick; +} + +struct fragment { + equeue_t *q; + size_t size; + struct timing timing; +}; + +static void fragment_func(void *p) +{ + struct fragment *fragment = reinterpret_cast(p); + timing_func(&fragment->timing); + + struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); + ASSERT_TRUE(nfragment != NULL); + + *nfragment = *fragment; + equeue_event_delay(nfragment, fragment->timing.delay); + + int id = equeue_post(nfragment->q, fragment_func, nfragment); + ASSERT_NE(0, id); +} + +struct cancel { + equeue_t *q; + int id; +}; + +static void cancel_func(void *p) +{ + struct cancel *ccel = reinterpret_cast(p); + equeue_cancel(ccel->q, ccel->id); +} + +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +static void nest_func(void *p) +{ + struct nest *nst = reinterpret_cast(p); + equeue_call(nst->q, nst->cb, nst->data); + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; +} + +static void *multithread_thread(void *p) +{ + equeue_t *q = reinterpret_cast(p); + equeue_dispatch(q, DISPATCH_INFINITE); + return 0; +} + +class ecount { + mutable pthread_mutex_t mutex; + pthread_cond_t cond; + uint8_t count; +public: + ecount() : count(0) + { + int err = pthread_mutex_init(&mutex, NULL); + EXPECT_EQ(0, err); + err = pthread_cond_init(&cond, NULL); + EXPECT_EQ(0, err); + } + + ~ecount() + { + int err = pthread_mutex_destroy(&mutex); + EXPECT_EQ(0, err); + err = pthread_cond_destroy(&cond); + EXPECT_EQ(0, err); + } + + void lock() const + { + int err = pthread_mutex_lock(&mutex); + EXPECT_EQ(0, err); + } + + void unlock() const + { + int err = pthread_mutex_unlock(&mutex); + EXPECT_EQ(0, err); + } + + void touch() + { + lock(); + if (count < 200) { + count++; + } + unlock(); + int err = pthread_cond_broadcast(&cond); + EXPECT_EQ(0, err); + } + + void wait_for_touches(uint8_t n) + { + lock(); + while (count < n) { + int err = pthread_cond_wait(&cond, &mutex); + EXPECT_EQ(0, err); + } + unlock(); + } +}; + +static void multithread_func(void *p) +{ + static_cast(p)->touch(); +} + +static void background_func(void *p, int ms) +{ + *(reinterpret_cast(p)) = ms; +} + +struct ethread { + pthread_t thread; + equeue_t *q; + int ms; +}; + +static void *ethread_dispatch(void *p) +{ + struct ethread *t = reinterpret_cast(p); + equeue_dispatch(t->q, t->ms); + return 0; +} + +struct count_and_queue { + int p; + equeue_t *q; +}; + +static void simple_breaker(void *p) +{ + struct count_and_queue *caq = reinterpret_cast(p); + equeue_break(caq->q); + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; + caq->p++; +} + +// Simple call tests + +/** Test that equeue executes function passed by equeue_call. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_call) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 10); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_in. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_in is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_call_in) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int id = equeue_call_in(&q, 10, simple_func, &touched); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 15); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 10); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_every. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_every is executed properly. + */ + +TEST_F(TestEqueue, test_equeue_simple_call_every) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int id = equeue_call_every(&q, 10, simple_func, &touched); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 15); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_post. + * + * Given queue is initialized. + * When the event is posted and after that equeue_dispatch is called. + * Then function passed by equeue_post is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_post) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(i != NULL); + + i->touched = &touched; + int id = equeue_post(&q, indirect_func, i); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, *i->touched); + + equeue_destroy(&q); +} + +// Misc tests + +/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. + * + * Given queue is initialized. + * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. + * Then functions attached to equeue events destructors are executed properly. + */ +TEST_F(TestEqueue, test_equeue_destructor) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct indirect *e; + int ids[3]; + + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e != NULL); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(3, touched); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e != NULL); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + ids[i] = equeue_post(&q, pass_func, e); + ASSERT_NE(0, ids[i]); + } + + for (int i = 0; i < 3; i++) { + equeue_cancel(&q, ids[i]); + } + EXPECT_EQ(3, touched); + + equeue_dispatch(&q, 0); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + ASSERT_NE(0, id); + } + + equeue_destroy(&q); + EXPECT_EQ(3, touched); +} + +/** Test that equeue_alloc returns 0 when equeue can not be allocated. + * + * Given queue is initialized. + * When equeue_alloc is called and equeue can not be allocated + * Then function equeue_alloc returns NULL. + */ +TEST_F(TestEqueue, test_equeue_allocation_failure) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); + EXPECT_TRUE(p == NULL); + + for (int i = 0; i < 100; i++) { + p = equeue_alloc(&q, 0); + } + EXPECT_TRUE(p == NULL); + + equeue_destroy(&q); +} + +/** Test that equeue does not execute evenets that has been canceled. + * + * Given queue is initialized. + * When events are canceled by equeue_cancel. + * Then they are not executed by calling equeue_dispatch. + */ +TEST_F(TestEqueue, test_equeue_cancel) +{ + equeue_t q; + int err = equeue_create(&q, (2 * ITERATION_TIMES * EVENTS_EVENT_SIZE)); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int ids[2 * ITERATION_TIMES]; + + for (int i = 0; i < 2 * ITERATION_TIMES; i++) { + ids[i] = equeue_call(&q, simple_func, &touched); + ASSERT_NE(0, ids[i]); + } + + for (int i = 2 * ITERATION_TIMES - 1; i >= 0; i--) { + equeue_cancel(&q, ids[i]); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that events can be cancelled by function executed by equeue_dispatch. + * + * Given queue is initialized. + * When event is cancelled by another event while dispatching. + * Then event that was cancelled is not being executed. + */ +TEST_F(TestEqueue, test_equeue_cancel_inflight) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + + int id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); + ASSERT_TRUE(ccel != NULL); + ccel->q = &q; + ccel->id = 0; + + id = equeue_post(&q, cancel_func, ccel); + ASSERT_NE(0, id); + + ccel->id = equeue_call(&q, simple_func, &touched); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that unnecessary canceling events would not affect executing other events. + * + * Given queue is initialized. + * When event is unnecessary canceled by equeue_cancel. + * Then other events are properly executed after calling equeue_dispatch. + */ +TEST_F(TestEqueue, test_equeue_cancel_unnecessarily) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id = equeue_call(&q, pass_func, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + id = equeue_call(&q, pass_func, 0); + equeue_dispatch(&q, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. + * + * Given queue is initialized. + * When events have 0 ms period time. + * Then dispatching would not end up in infinite loop. + */ +TEST_F(TestEqueue, test_equeue_loop_protect) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched1); + + touched1 = 0; + uint8_t touched2 = 0; + equeue_call_every(&q, 1, simple_func, &touched2); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break breaks event queue out of dispatching. + * + * Given queue is initialized. + * When equeue_break is called. + * Then event queue will stop dispatching after finisching current dispatching cycle. + */ +TEST_F(TestEqueue, test_equeue_break) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + uint8_t touched2 = 0; + equeue_call_every(&q, 5, simple_func, &touched2); + + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break function breaks equeue dispatching only once. + * + * Given queue is initialized. + * When equeue_break is called several times. + * Then equeue is stopped only once. + */ +TEST_F(TestEqueue, test_equeue_break_no_windup) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_break(&q); + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 55); + EXPECT_TRUE(touched > 1); + + equeue_destroy(&q); +} + +/** Test that function passed by equeue_call_every is being executed periodically. + * + * Given queue is initialized. + * When function is passed by equeue_call_every with specified period. + * Then event is executed (dispatch time/period) times. + */ + +TEST_F(TestEqueue, test_equeue_period) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + EXPECT_EQ(5, touched); + + equeue_destroy(&q); +} + +/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. + * + * Given queue is initialized. + * When nested function is added to enqueue. + * Then it is executed in the next dispatch, or after execution of "mother" function. + */ +TEST_F(TestEqueue, test_equeue_nested) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + ASSERT_TRUE(nst != NULL); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + int id = equeue_post(&q, nest_func, nst); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 5); + EXPECT_EQ(0, touched); + + equeue_dispatch(&q, 1); + EXPECT_EQ(1, touched); + + touched = 0; + nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + ASSERT_TRUE(nst != NULL); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + id = equeue_post(&q, nest_func, nst); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 20); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. + * + * Given queue is initialized. + * When sloth function is being called before other functions. + * Then if it is possible all functions start according to predefined schedule correctly. + */ +TEST_F(TestEqueue, test_equeue_sloth) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + uint8_t touched3 = 0; + int id = equeue_call(&q, sloth_func, &touched1); + ASSERT_NE(0, id); + + id = equeue_call_in(&q, 5, simple_func, &touched2); + ASSERT_NE(0, id); + + id = equeue_call_in(&q, 15, simple_func, &touched3); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 20); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + EXPECT_EQ(1, touched3); + + equeue_destroy(&q); +} + +/** Test that equeue can be broken of dispatching from a different thread. + * + * Given queue is initialized. + * When equeue starts dispatching in one thread. + * Then it can be stopped from another thread via equeue_break. + */ +TEST_F(TestEqueue, test_equeue_multithread) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + ecount t; + equeue_call_every(&q, 1, multithread_func, &t); + + pthread_t thread; + err = pthread_create(&thread, 0, multithread_thread, &q); + ASSERT_EQ(0, err); + + t.wait_for_touches(1); + equeue_break(&q); + err = pthread_join(thread, 0); + ASSERT_EQ(0, err); + + equeue_destroy(&q); +} + +/** Test that variable referred via equeue_background shows value in ms to the next event. + * + * Given queue is initialized. + * When variable is referred via equeue_background. + * Then it depicts the time in ms to the next event. + */ +TEST_F(TestEqueue, test_equeue_background) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id = equeue_call_in(&q, 20, pass_func, 0); + ASSERT_NE(0, id); + + int ms; + equeue_background(&q, background_func, &ms); + EXPECT_EQ(20, ms); + + id = equeue_call_in(&q, 10, pass_func, 0); + ASSERT_NE(0, id); + EXPECT_EQ(10, ms); + + id = equeue_call(&q, pass_func, 0); + ASSERT_NE(0, id); + EXPECT_EQ(0, ms); + + equeue_dispatch(&q, 0); + EXPECT_EQ(10, ms); + + equeue_destroy(&q); + EXPECT_EQ(-1, ms); +} + +/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. + * + * Given queue is initialized. + * When target chained equeue is dispatched. + * Then events from both chained equeues are executed. + */ +TEST_F(TestEqueue, test_equeue_chain) +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); + ASSERT_NE(0, id1); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); + ASSERT_NE(0, id2); + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call_in(&q2, 5, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call_in(&q2, 10, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 30); + + EXPECT_EQ(3, touched1); + EXPECT_EQ(3, touched2); + + equeue_destroy(&q2); + equeue_destroy(&q1); +} + +/** Test that unchaining equeues makes them work on their own. + * + * Given queue is initialized. + * When equeue is unchained. + * Then it can be only dispatched by calling with reference to it. + */ +TEST_F(TestEqueue, test_equeue_unchain) +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + int id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + int id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_chain(&q2, 0); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_dispatch(&q2, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_chain(&q1, &q2); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q2, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_destroy(&q1); + equeue_destroy(&q2); +} + +// Barrage tests + +/** Test that equeue keeps good time at starting events. + * + * Given queue is initialized. + * When equeue is being dispatched. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_simple_barrage) +{ + equeue_t q; + int err = equeue_create(&q, 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + ASSERT_EQ(0, err); + + for (int i = 0; i < 2 * ITERATION_TIMES; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + ASSERT_TRUE(timing != NULL); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, 2 * ITERATION_TIMES * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. + * + * Given queue is initialized. + * When equeue is being dispatched and new events are added via already placed in equeue. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_fragmenting_barrage) +{ + equeue_t q; + int err = equeue_create(&q, + 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + ITERATION_TIMES * sizeof(int))); + ASSERT_EQ(0, err); + + for (int i = 0; i < ITERATION_TIMES; i++) { + size_t size = sizeof(struct fragment) + i * sizeof(int); + struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); + ASSERT_TRUE(fragment != NULL); + + fragment->q = &q; + fragment->size = size; + fragment->timing.tick = equeue_tick(); + fragment->timing.delay = (i + 1) * 100; + equeue_event_delay(fragment, fragment->timing.delay); + + int id = equeue_post(&q, fragment_func, fragment); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, ITERATION_TIMES * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events even if it is working on different thread. + * + * Given queue is initialized. + * When equeue is being dispatched on different thread. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_multithreaded_barrage) +{ + equeue_t q; + int err = equeue_create(&q, ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + ASSERT_EQ(0, err); + + struct ethread t; + t.q = &q; + t.ms = ITERATION_TIMES * 100; + + err = pthread_create(&t.thread, 0, ethread_dispatch, &t); + ASSERT_EQ(0, err); + + for (int i = 0; i < ITERATION_TIMES; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + ASSERT_TRUE(timing != NULL); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + ASSERT_NE(0, id); + } + + err = pthread_join(t.thread, 0); + ASSERT_EQ(0, err); + + equeue_destroy(&q); +} + +/** Test that break request flag is cleared when equeue stops dispatching timeouts. + * + * Given queue is initialized. + * When equeue break request flag is called but equeue stops dispatching because of timeout. + * Then next equeue dispatch is not stopped. + */ +TEST_F(TestEqueue, test_equeue_break_request_cleared_on_timeout) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + struct count_and_queue pq; + pq.p = 0; + pq.q = &q; + + int id = equeue_call_in(&q, 1, simple_breaker, &pq); + + equeue_dispatch(&q, 10); + + EXPECT_EQ(1, pq.p); + + equeue_cancel(&q, id); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + EXPECT_EQ(5, touched); + + equeue_destroy(&q); +} + +/** Test that siblings events don't have next pointers. + * + * Given queue is initialized. + * When events are scheduled on the same time. + * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. + */ +TEST_F(TestEqueue, test_equeue_sibling) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id0 = equeue_call_in(&q, 1, pass_func, 0); + int id1 = equeue_call_in(&q, 1, pass_func, 0); + int id2 = equeue_call_in(&q, 1, pass_func, 0); + + struct equeue_event *e = q.queue; + + for (; e; e = e->next) { + for (struct equeue_event *s = e->sibling; s; s = s->sibling) { + EXPECT_TRUE(s->next == NULL); + } + } + equeue_cancel(&q, id0); + equeue_cancel(&q, id1); + equeue_cancel(&q, id2); + equeue_destroy(&q); +} + +/** Test that equeue executes user allocated events passed by equeue_post. + * + * Given queue is initialized and its size is set to store one event at max in its internal memory. + * When post events allocated in queues internal memory (what is done by calling equeue_call). + * Then only one event can be posted due to queue memory size. + * When post user allocated events. + * Then number of posted events is not limited by queue memory size. + * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. + * Then both types of events are executed properly. + */ +TEST_F(TestEqueue, test_equeue_user_allocated_event_post) +{ + struct user_allocated_event { + struct equeue_event e; + uint8_t touched; + }; + equeue_t q; + int err = equeue_create(&q, EQUEUE_EVENT_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + + EXPECT_NE(0, equeue_call_every(&q, 10, simple_func, &touched)); + EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); + EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); + + equeue_post_user_allocated(&q, simple_func, &e1.e); + equeue_post_user_allocated(&q, simple_func, &e2.e); + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_post_user_allocated(&q, simple_func, &e4.e); + equeue_post_user_allocated(&q, simple_func, &e5.e); + equeue_cancel_user_allocated(&q, &e3.e); + equeue_cancel_user_allocated(&q, &e3.e); + + equeue_dispatch(&q, 11); + + EXPECT_EQ(1, touched); + EXPECT_EQ(1, e1.touched); + EXPECT_EQ(1, e2.touched); + EXPECT_EQ(0, e3.touched); + EXPECT_EQ(1, e4.touched); + EXPECT_EQ(1, e5.touched); + + e3.e.target = 10; // set target as it's modified by equeue_call + e3.e.period = 10; // set period as it's reset by equeue_cancel + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_dispatch(&q, 101); + + EXPECT_EQ(11, touched); + EXPECT_EQ(1 , e1.touched); + EXPECT_EQ(11, e2.touched); + EXPECT_EQ(10 , e3.touched); + EXPECT_EQ(11, e4.touched); + EXPECT_EQ(1 , e5.touched); + + equeue_destroy(&q); +} diff --git a/events/tests/UNITTESTS/equeue/unittest.cmake b/events/tests/UNITTESTS/equeue/unittest.cmake new file mode 100644 index 0000000..fe0f05a --- /dev/null +++ b/events/tests/UNITTESTS/equeue/unittest.cmake @@ -0,0 +1,27 @@ + +#################### +# UNIT TESTS +#################### + +list(REMOVE_ITEM unittest-includes ${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h ${PROJECT_SOURCE_DIR}/../events/test/UNITTESTS/target_h/equeue) + +set(unittest-includes ${unittest-includes} + ../events/source + ../events/include/events + ../events/include/events/internal +) + +set(unittest-sources + ../events/source/equeue.c +) + +set(unittest-test-sources + ../events/tests/UNITTESTS/equeue/test_equeue.cpp + ../events/tests/UNITTESTS/stubs/EqueuePosix_stub.c +) + +set(unittest-test-flags + -pthread + -DEQUEUE_PLATFORM_POSIX +) + diff --git a/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c b/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c new file mode 100644 index 0000000..e3d975e --- /dev/null +++ b/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal/equeue_platform.h" + +#if defined(EQUEUE_PLATFORM_POSIX) + +#include + +/* + * Using global variable as a simulation of passing time. Use of sleep functions may cause problems with thread preempting, which can lead to bad timings. + * This problem does not occur on targets. + * This variable is only increased in equeue_sema_wait function and in functions that simulate pass of time. + */ +unsigned int equeue_global_time = 0; + +// Tick operations +void equeue_tick_init(void) {} + +unsigned equeue_tick(void) +{ + return equeue_global_time; +} + + +// Mutex operations +int equeue_mutex_create(equeue_mutex_t *m) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + return pthread_mutex_init(m, &attr); +} + +void equeue_mutex_destroy(equeue_mutex_t *m) +{ + pthread_mutex_destroy(m); +} + +void equeue_mutex_lock(equeue_mutex_t *m) +{ + pthread_mutex_lock(m); +} + +void equeue_mutex_unlock(equeue_mutex_t *m) +{ + pthread_mutex_unlock(m); +} + + +// Semaphore operations +int equeue_sema_create(equeue_sema_t *s) +{ + int err = pthread_mutex_init(&s->mutex, 0); + if (err) { + return err; + } + + err = pthread_cond_init(&s->cond, 0); + if (err) { + return err; + } + + s->signal = false; + return 0; +} + +void equeue_sema_destroy(equeue_sema_t *s) +{ + pthread_cond_destroy(&s->cond); + pthread_mutex_destroy(&s->mutex); +} + +void equeue_sema_signal(equeue_sema_t *s) +{ + pthread_mutex_lock(&s->mutex); + s->signal = true; + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->mutex); +} + +bool equeue_sema_wait(equeue_sema_t *s, int ms) +{ + pthread_mutex_lock(&s->mutex); + if (!s->signal) { + if (ms < 0) { + pthread_cond_wait(&s->cond, &s->mutex); + } else { + for (int i = 0; i < ms; i++) { + equeue_global_time++; + } + // If ms == 0 increase time so functions don't get stuck in infinite loops. + if (ms == 0) { + equeue_global_time++; + } + } + } + + bool signal = s->signal; + s->signal = false; + pthread_mutex_unlock(&s->mutex); + + return signal; +} + +#endif diff --git a/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h b/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h new file mode 100644 index 0000000..7d9f968 --- /dev/null +++ b/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EQUEUE_PLATFORM_H +#define EQUEUE_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef unsigned equeue_mutex_t; + +typedef struct equeue_sema { +} equeue_sema_t; + +#ifdef __cplusplus +} +#endif + +#endif //EQUEUE_PLATFORM_H diff --git a/events/tests/UNITTESTS/target_h/mbed_events.h b/events/tests/UNITTESTS/target_h/mbed_events.h new file mode 100644 index 0000000..c104e73 --- /dev/null +++ b/events/tests/UNITTESTS/target_h/mbed_events.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_EVENTS_H +#define MBED_EVENTS_H + +#include "events/EventQueue.h" + +#include "events/mbed_shared_queues.h" + +#endif diff --git a/events/tests/unit/Makefile b/events/tests/unit/Makefile new file mode 100644 index 0000000..777d315 --- /dev/null +++ b/events/tests/unit/Makefile @@ -0,0 +1,55 @@ +TARGET = libequeue.a + +CC = gcc +AR = ar +SIZE = size + +SRC += $(wildcard ../../source/*.c) +OBJ := $(SRC:.c=.o) +DEP := $(SRC:.c=.d) +ASM := $(SRC:.c=.s) + +ifdef DEBUG +CFLAGS += -O0 -g3 +else +CFLAGS += -O2 +endif +ifdef WORD +CFLAGS += -m$(WORD) +endif +CFLAGS += -I. -I../../include +CFLAGS += -std=c99 +CFLAGS += -Wall +CFLAGS += -D_XOPEN_SOURCE=600 + +LFLAGS += -pthread + + +all: $(TARGET) + +prof: prof.o $(OBJ) + $(CC) $(CFLAGS) $^ $(LFLAGS) -o prof + ./prof + +asm: $(ASM) + +size: $(OBJ) + $(SIZE) -t $^ + +-include $(DEP) + +%.a: $(OBJ) + $(AR) rcs $@ $^ + +%.o: %.c + $(CC) -c -MMD $(CFLAGS) $< -o $@ + +%.s: %.c + $(CC) -S $(CFLAGS) $< -o $@ + +clean: + rm -f $(TARGET) + rm -f prof prof.o prof.d + rm -f $(OBJ) + rm -f $(DEP) + rm -f $(ASM) diff --git a/events/tests/unit/prof.c b/events/tests/unit/prof.c new file mode 100644 index 0000000..f7b8f90 --- /dev/null +++ b/events/tests/unit/prof.c @@ -0,0 +1,424 @@ +/* + * Profiling framework for the events library + * + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "events/equeue.h" +#include +#include +#include +#include +#include +#include +#include + + +// Performance measurement utils +#define PROF_RUNS 5 +#define PROF_INTERVAL 100000000 + +#define prof_volatile(t) __attribute__((unused)) volatile t + +typedef uint64_t prof_cycle_t; + +static volatile prof_cycle_t prof_start_cycle; +static volatile prof_cycle_t prof_stop_cycle; +static prof_cycle_t prof_accum_cycle; +static prof_cycle_t prof_baseline_cycle; +static prof_cycle_t prof_iterations; +static const char *prof_units; + +#define prof_cycle() ({ \ + uint32_t a, b; \ + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (b)); \ + ((uint64_t)b << 32) | (uint64_t)a; \ +}) + +#define prof_loop() \ + for (prof_iterations = 0; \ + prof_accum_cycle < PROF_INTERVAL; \ + prof_iterations++) + +#define prof_start() ({ \ + prof_start_cycle = prof_cycle(); \ +}) + +#define prof_stop() ({ \ + prof_stop_cycle = prof_cycle(); \ + prof_accum_cycle += prof_stop_cycle - prof_start_cycle; \ +}) + +#define prof_result(value, units) ({ \ + prof_accum_cycle = value+prof_baseline_cycle; \ + prof_iterations = 1; \ + prof_units = units; \ +}) + +#define prof_measure(func, ...) ({ \ + printf("%s: ...", #func); \ + fflush(stdout); \ + \ + prof_units = "cycles"; \ + prof_cycle_t runs[PROF_RUNS]; \ + for (int i = 0; i < PROF_RUNS; i++) { \ + prof_accum_cycle = 0; \ + prof_iterations = 0; \ + func(__VA_ARGS__); \ + runs[i] = prof_accum_cycle / prof_iterations; \ + } \ + \ + prof_cycle_t res = runs[0]; \ + for (int i = 0; i < PROF_RUNS; i++) { \ + if (runs[i] < res) { \ + res = runs[i]; \ + } \ + } \ + res -= prof_baseline_cycle; \ + printf("\r%s: %"PRIu64" %s", #func, res, prof_units); \ + \ + if (!isatty(0)) { \ + prof_cycle_t prev; \ + while (scanf("%*[^0-9]%"PRIu64, &prev) == 0); \ + int64_t perc = 100*((int64_t)prev - (int64_t)res) / (int64_t)prev; \ + \ + if (perc > 10) { \ + printf(" (\e[32m%+"PRId64"%%\e[0m)", perc); \ + } else if (perc < -10) { \ + printf(" (\e[31m%+"PRId64"%%\e[0m)", perc); \ + } else { \ + printf(" (%+"PRId64"%%)", perc); \ + } \ + } \ + \ + printf("\n"); \ + res; \ +}) + +#define prof_baseline(func, ...) ({ \ + prof_baseline_cycle = 0; \ + prof_baseline_cycle = prof_measure(func, __VA_ARGS__); \ +}) + + +// Various test functions +void no_func(void *eh) +{ +} + + +// Actual performance tests +void baseline_prof(void) +{ + prof_loop() { + prof_start(); + __asm__ volatile(""); + prof_stop(); + } +} + +void equeue_tick_prof(void) +{ + prof_volatile(unsigned) res; + prof_loop() { + prof_start(); + res = equeue_tick(); + prof_stop(); + } +} + +void equeue_alloc_prof(void) +{ + struct equeue q; + equeue_create(&q, 32 * EQUEUE_EVENT_SIZE); + + prof_loop() { + prof_start(); + void *e = equeue_alloc(&q, 8 * sizeof(int)); + prof_stop(); + + equeue_dealloc(&q, e); + } + + equeue_destroy(&q); +} + +void equeue_alloc_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + void *es[count]; + + for (int i = 0; i < count; i++) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = 0; i < count; i++) { + equeue_dealloc(&q, es[i]); + } + + prof_loop() { + prof_start(); + void *e = equeue_alloc(&q, 8 * sizeof(int)); + prof_stop(); + + equeue_dealloc(&q, e); + } + + equeue_destroy(&q); +} + +void equeue_post_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + void *e = equeue_alloc(&q, 0); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + void *e = equeue_alloc(&q, 0); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_future_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + void *e = equeue_alloc(&q, 0); + equeue_event_delay(e, 1000); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_future_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + void *e = equeue_alloc(&q, 0); + equeue_event_delay(e, 1000); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_dispatch_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + equeue_call(&q, no_func, 0); + + prof_start(); + equeue_dispatch(&q, 0); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_dispatch_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + prof_loop() { + for (int i = 0; i < count; i++) { + equeue_call(&q, no_func, 0); + } + + prof_start(); + equeue_dispatch(&q, 0); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_cancel_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + int id = equeue_call(&q, no_func, 0); + + prof_start(); + equeue_cancel(&q, id); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_cancel_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + int id = equeue_call(&q, no_func, 0); + + prof_start(); + equeue_cancel(&q, id); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_alloc_size_prof(void) +{ + size_t size = 32 * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + equeue_alloc(&q, 0); + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + +void equeue_alloc_many_size_prof(int count) +{ + size_t size = count * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + + for (int i = 0; i < count; i++) { + equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + +void equeue_alloc_fragmented_size_prof(int count) +{ + size_t size = count * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + + void *es[count]; + + for (int i = 0; i < count; i++) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = 0; i < count; i++) { + equeue_dealloc(&q, es[i]); + } + + for (int i = count - 1; i >= 0; i--) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = count - 1; i >= 0; i--) { + equeue_dealloc(&q, es[i]); + } + + for (int i = 0; i < count; i++) { + equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + + +// Entry point +int main() +{ + printf("beginning profiling...\n"); + + prof_baseline(baseline_prof); + + prof_measure(equeue_tick_prof); + prof_measure(equeue_alloc_prof); + prof_measure(equeue_post_prof); + prof_measure(equeue_post_future_prof); + prof_measure(equeue_dispatch_prof); + prof_measure(equeue_cancel_prof); + + prof_measure(equeue_alloc_many_prof, 1000); + prof_measure(equeue_post_many_prof, 1000); + prof_measure(equeue_post_future_many_prof, 1000); + prof_measure(equeue_dispatch_many_prof, 100); + prof_measure(equeue_cancel_many_prof, 100); + + prof_measure(equeue_alloc_size_prof); + prof_measure(equeue_alloc_many_size_prof, 1000); + prof_measure(equeue_alloc_fragmented_size_prof, 1000); + + printf("done!\n"); +} diff --git a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp index 783405d..1a86b7d 100644 --- a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp +++ b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp @@ -24,9 +24,9 @@ #include "platform/SingletonPtr.h" #include "platform/arm_hal_interrupt.h" #include "platform/mbed_power_mgmt.h" -#include "equeue.h" +#include "events/equeue.h" #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "Timeout.h" #define TRACE_GROUP "fhdr" diff --git a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp index 7921e5b..dc274db 100644 --- a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp +++ b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp @@ -27,7 +27,7 @@ #include "eventOS_scheduler.h" #include "mbed_error.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "events/Event.h" #include "ns_event_loop_mutex.h" #include "ns_event_loop.h"