From 13de62bd4f06dbd6a8944894500adf6037f84fa4 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 21 Oct 2017 09:24:16 +0100 Subject: [PATCH] Merge remote-tracking branch 'origin/development' # Conflicts: # library.properties # src/callback_timer.h # src/message_timer.h # test/test_callback_timer.cpp # test/test_message_timer.cpp --- .gitignore | 4 + library.properties | 2 +- src/callback_timer.h | 60 +++++++------- src/message_timer.h | 36 ++++---- test/test_callback_timer.cpp | 154 ++++++++++++++++++++++++++++++++++- test/test_message_timer.cpp | 13 +++ 6 files changed, 217 insertions(+), 52 deletions(-) diff --git a/.gitignore b/.gitignore index f9086ebd..07a9f262 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ test/keil/Debug test/keil/Release test/keil/Objects test/keil/Listings +examples/ArmTimerCallbacks/Listings +examples/ArmTimerCallbacks/Objects +examples/ArmTimerCallbacks/DebugConfig ################# ## Eclipse @@ -27,6 +30,7 @@ local.properties .classpath .settings/ .loadpath +*.uvguix.John # External tool builders .externalToolBuilders/ diff --git a/library.properties b/library.properties index 28e0d889..0b7bd310 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Embedded Template Library -version=10.0.1 +version=10.0.2 author= John Wellbelove maintainer=John Wellbelove sentence=A C++ template library tailored for embedded systems. diff --git a/src/callback_timer.h b/src/callback_timer.h index 5fc1ab7a..d8e5124c 100644 --- a/src/callback_timer.h +++ b/src/callback_timer.h @@ -57,7 +57,7 @@ namespace etl previous(etl::timer::id::NO_TIMER), next(etl::timer::id::NO_TIMER), repeating(true), - is_c_callback(true) + has_c_callback(true) { } @@ -75,7 +75,7 @@ namespace etl previous(etl::timer::id::NO_TIMER), next(etl::timer::id::NO_TIMER), repeating(repeating_), - is_c_callback(true) + has_c_callback(true) { } @@ -93,7 +93,7 @@ namespace etl previous(etl::timer::id::NO_TIMER), next(etl::timer::id::NO_TIMER), repeating(repeating_), - is_c_callback(false) + has_c_callback(false) { } @@ -120,7 +120,7 @@ namespace etl uint_least8_t previous; uint_least8_t next; bool repeating; - bool is_c_callback; + bool has_c_callback; private: @@ -453,7 +453,6 @@ namespace etl } registered_timers = 0; - tick_count = 0; enable_timer_updates(); } @@ -468,42 +467,53 @@ namespace etl { if (enabled) { - if (process_semaphore == 0) + if (process_semaphore.load() == 0) { // We have something to do? - if (!active_list.empty()) - { - tick_count += count; + bool has_active = !active_list.empty(); - while (!active_list.empty() && (tick_count >= active_list.front().delta)) + if (has_active) + { + while (has_active && (count >= active_list.front().delta)) { etl::callback_timer_data& timer = active_list.front(); - tick_count -= timer.delta; + count -= timer.delta; active_list.remove(timer.id, true); if (timer.repeating) { + // Reinsert the timer. timer.delta = timer.period; active_list.insert(timer.id); } if (timer.p_callback != nullptr) { - if (timer.is_c_callback) + if (timer.has_c_callback) { + // Call the C callback. reinterpret_cast(timer.p_callback)(); } else { + // Call the function wrapper callback. (*reinterpret_cast*>(timer.p_callback))(); } } + + has_active = !active_list.empty(); } - return true; + if (has_active) + { + // Subtract any remainder from the next due timeout. + active_list.front().delta -= count; + } } + + return true; } } @@ -515,10 +525,10 @@ namespace etl //******************************************* bool start(etl::timer::id::type id_, bool immediate_ = false) { - bool result = false; - disable_timer_updates(); + bool result = false; + // Valid timer id? if (id_ != etl::timer::id::NO_TIMER) { @@ -526,7 +536,7 @@ namespace etl // Registered timer? if (timer.id != etl::timer::id::NO_TIMER) - { + { // Has a valid period. if (timer.period != etl::timer::state::INACTIVE) { @@ -535,17 +545,11 @@ namespace etl active_list.remove(timer.id, false); } - // Compensate for current tick count. - timer.delta = tick_count; - - if (!immediate_) - { - timer.delta += timer.period; - } - + timer.delta = immediate_ ? 0 : timer.period; active_list.insert(timer.id); + result = true; - } + } } } @@ -559,10 +563,10 @@ namespace etl //******************************************* bool stop(etl::timer::id::type id_) { - bool result = false; - disable_timer_updates(); + bool result = false; + // Valid timer id? if (id_ != etl::timer::id::NO_TIMER) { @@ -622,7 +626,6 @@ namespace etl active_list(timer_array_), enabled(false), process_semaphore(0), - tick_count(0), registered_timers(0), MAX_TIMERS(MAX_TIMERS_) { @@ -654,7 +657,6 @@ namespace etl volatile bool enabled; volatile etl::timer_semaphore_t process_semaphore; - volatile uint32_t tick_count; volatile uint_least8_t registered_timers; public: diff --git a/src/message_timer.h b/src/message_timer.h index 39ca2773..b1433d42 100644 --- a/src/message_timer.h +++ b/src/message_timer.h @@ -40,6 +40,7 @@ SOFTWARE. #include "message_bus.h" #include "static_assert.h" #include "timer.h" +#include "atomic.h" #undef ETL_FILE #define ETL_FILE "41" @@ -417,7 +418,6 @@ namespace etl } registered_timers = 0; - tick_count = 0; enable_timer_updates(); } @@ -432,18 +432,18 @@ namespace etl { if (enabled) { - if (process_semaphore == 0) + if (process_semaphore.load() == 0) { // We have something to do? - if (!active_list.empty()) - { - tick_count += count; + bool has_active = !active_list.empty(); - while (!active_list.empty() && (tick_count >= active_list.front().delta)) + if (has_active) + { + while (has_active && (count >= active_list.front().delta)) { etl::message_timer_data& timer = active_list.front(); - tick_count -= timer.delta; + count -= timer.delta; active_list.remove(timer.id, true); @@ -467,10 +467,18 @@ namespace etl timer.p_router->receive(*(timer.p_message)); } } + + has_active = !active_list.empty(); } - return true; + if (has_active) + { + // Subtract any remainder from the next due timeout. + active_list.front().delta -= count; + } } + + return true; } } @@ -502,15 +510,9 @@ namespace etl active_list.remove(timer.id, false); } - // Compensate for current tick count. - timer.delta = tick_count; - - if (!immediate_) - { - timer.delta += timer.period; - } - + timer.delta = immediate_ ? 0 : timer.period; active_list.insert(timer.id); + result = true; } } @@ -589,7 +591,6 @@ namespace etl active_list(timer_array_), enabled(false), process_semaphore(0), - tick_count(0), registered_timers(0), MAX_TIMERS(MAX_TIMERS_) { @@ -621,7 +622,6 @@ namespace etl volatile bool enabled; volatile etl::timer_semaphore_t process_semaphore; - volatile uint32_t tick_count; volatile uint_least8_t registered_timers; public: diff --git a/test/test_callback_timer.cpp b/test/test_callback_timer.cpp index ff3d9198..eed41124 100644 --- a/test/test_callback_timer.cpp +++ b/test/test_callback_timer.cpp @@ -63,11 +63,27 @@ namespace tick_list.push_back(ticks); } + void callback2() + { + tick_list.push_back(ticks); + + p_controller->start(2); + p_controller->start(1); + } + + void set_controller(etl::callback_timer<3>& controller) + { + p_controller = &controller; + } + std::vector tick_list; + + etl::callback_timer<3>* p_controller; }; Test test; - etl::function_mv member_callback(test); + etl::function_imv member_callback; + etl::function_imv member_callback2; //*************************************************************************** // Free function callback via etl::function @@ -144,6 +160,10 @@ namespace std::vector compare2 = { 23 }; std::vector compare3 = { 11 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -182,6 +202,10 @@ namespace std::vector compare2 = { 23, 46, 69, 92 }; std::vector compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -224,6 +248,10 @@ namespace std::vector compare2 = { 25, 50, 70, 95 }; std::vector compare3 = { 15, 25, 35, 45, 55, 70, 80, 90, 100 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -272,12 +300,90 @@ namespace std::vector compare1 = { 77 }; std::vector compare2 = { 23 }; std::vector compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 }; + + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); } + //========================================================================= + TEST(callback_timer_timer_starts_timer_small_step) + { + etl::callback_timer<3> timer_controller; + + etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::SINGLE_SHOT); + etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::SINGLE_SHOT); + etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::SINGLE_SHOT); + + (void)id2; + (void)id3; + + test.set_controller(timer_controller); + + test.tick_list.clear(); + + timer_controller.start(id1); + + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 1; + + while (ticks <= 200U) + { + ticks += step; + timer_controller.tick(step); + } + + std::vector compare1 = { 100, 110, 122 }; + + CHECK(test.tick_list.size() != 0); + + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); + } + + //========================================================================= + TEST(callback_timer_timer_starts_timer_big_step) + { + etl::callback_timer<3> timer_controller; + + etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::SINGLE_SHOT); + etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::SINGLE_SHOT); + etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::SINGLE_SHOT); + + (void)id2; + (void)id3; + + test.set_controller(timer_controller); + + test.tick_list.clear(); + + timer_controller.start(id1); + + timer_controller.enable(true); + + ticks = 0; + + const uint32_t step = 3; + + while (ticks <= 200U) + { + ticks += step; + timer_controller.tick(step); + } + + std::vector compare1 = { 102, 111, 123 }; + + CHECK(test.tick_list.size() != 0); + + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); + } + //========================================================================= TEST(callback_timer_repeating_register_unregister) { @@ -318,6 +424,10 @@ namespace std::vector compare2 = { 23 }; std::vector compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -362,6 +472,10 @@ namespace std::vector compare2 = { 23 }; std::vector compare3 = { 11, 22, 33 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -401,6 +515,10 @@ namespace std::vector compare2 = { 6, 28, 51, 74, 97 }; std::vector compare3 = { 16, 27, 38, 49, 60, 71, 82, 93 }; + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size()); CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size()); @@ -435,6 +553,9 @@ namespace std::vector compare1 = { 22 }; std::vector compare2 = { 11, 11, 22, 22 }; + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size()); CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list2.data(), compare2.size()); } @@ -474,11 +595,21 @@ namespace } std::vector compare1 = { 5, 10 }; + CHECK(free_tick_list1.size() != 0); + CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size()); } //========================================================================= #if REALTIME_TEST + + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define RAISE_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) + #define FIX_PROCESSOR_AFFINITY SetThreadAffinityMask(GetCurrentThread(), 1); + #else + #error No thread priority modifier defined + #endif + etl::callback_timer<3> controller; void timer_event() @@ -487,6 +618,9 @@ namespace uint32_t tick = TICK; ticks = 1; + RAISE_THREAD_PRIORITY; + FIX_PROCESSOR_AFFINITY; + while (ticks <= 1000) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -506,9 +640,11 @@ namespace TEST(callback_timer_threads) { + FIX_PROCESSOR_AFFINITY; + etl::timer::id::type id1 = controller.register_timer(member_callback, 400, etl::timer::mode::SINGLE_SHOT); etl::timer::id::type id2 = controller.register_timer(free_function_callback, 100, etl::timer::mode::REPEATING); - etl::timer::id::type id3 = controller.register_timer(free_callback2, 10, etl::timer::mode::REPEATING); + etl::timer::id::type id3 = controller.register_timer(free_callback2, 10, etl::timer::mode::REPEATING); test.tick_list.clear(); free_tick_list1.clear(); @@ -516,7 +652,7 @@ namespace controller.start(id1); controller.start(id2); - controller.start(id3); + //controller.start(id3); controller.enable(true); @@ -524,7 +660,7 @@ namespace bool restart_1 = true; - while (ticks < 1000U) + while (ticks <= 1000U) { if ((ticks > 200U) && (ticks < 500U)) { @@ -551,6 +687,16 @@ namespace CHECK_EQUAL(2U, test.tick_list.size()); CHECK_EQUAL(10U, free_tick_list1.size()); CHECK(free_tick_list2.size() < 65U); + + //std::vector compare1 = { 400, 900 }; + //std::vector compare2 = { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000 }; + + CHECK(test.tick_list.size() != 0); + CHECK(free_tick_list1.size() != 0); + CHECK(free_tick_list2.size() != 0); + + //CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), min(compare1.size(), test.tick_list.size())); + //CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), min(compare2.size(), free_tick_list1.size())); } #endif }; diff --git a/test/test_message_timer.cpp b/test/test_message_timer.cpp index 0f6c49e9..b3a9ddda 100644 --- a/test/test_message_timer.cpp +++ b/test/test_message_timer.cpp @@ -550,6 +550,14 @@ namespace //========================================================================= #if REALTIME_TEST + + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define RAISE_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) + #define FIX_PROCESSOR_AFFINITY SetThreadAffinityMask(GetCurrentThread(), 1); + #else + #error No thread priority modifier defined + #endif + etl::message_timer<3> controller; void timer_event() @@ -558,6 +566,9 @@ namespace uint32_t tick = TICK; ticks = 1; + RAISE_THREAD_PRIORITY; + FIX_PROCESSOR_AFFINITY; + while (ticks <= 1000) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -577,6 +588,8 @@ namespace TEST(message_timer_threads) { + FIX_PROCESSOR_AFFINITY; + etl::timer::id::type id1 = controller.register_timer(message1, router1, 400, etl::timer::mode::SINGLE_SHOT); etl::timer::id::type id2 = controller.register_timer(message2, router1, 100, etl::timer::mode::REPEATING); etl::timer::id::type id3 = controller.register_timer(message3, router1, 10, etl::timer::mode::REPEATING);