From 971f512ff9b7a002ef7d03e76b5df7b631e8b6b7 Mon Sep 17 00:00:00 2001 From: cameron Date: Sun, 15 Dec 2024 17:28:24 +1100 Subject: [PATCH 01/73] [#56] Fix update current thread priority for linux and make an enum --- src/Reactor.hpp | 4 +- src/dsl/Parse.hpp | 2 +- src/dsl/fusion/NoOp.hpp | 4 +- src/dsl/fusion/PriorityFusion.hpp | 4 +- src/dsl/word/Always.hpp | 2 +- src/dsl/word/Priority.hpp | 57 +++------------------ src/dsl/word/Shutdown.hpp | 3 +- src/extension/ChronoController.cpp | 2 +- src/threading/ReactionTask.hpp | 3 +- src/threading/scheduler/Group.cpp | 4 +- src/threading/scheduler/Group.hpp | 7 +-- src/threading/scheduler/Pool.cpp | 2 +- src/threading/scheduler/Scheduler.cpp | 2 +- src/threading/scheduler/Scheduler.hpp | 2 +- src/util/CallbackGenerator.hpp | 1 + src/util/Priority.hpp | 44 ++++++++++++++++ src/util/update_current_thread_priority.hpp | 34 +++++------- 17 files changed, 88 insertions(+), 89 deletions(-) create mode 100644 src/util/Priority.hpp diff --git a/src/Reactor.hpp b/src/Reactor.hpp index f072002e8..4f593ce67 100644 --- a/src/Reactor.hpp +++ b/src/Reactor.hpp @@ -51,6 +51,7 @@ namespace dsl { struct Once; + template struct Priority; template @@ -192,7 +193,8 @@ class Reactor { using Trigger = dsl::word::Trigger; /// @copydoc dsl::word::Priority - using Priority = dsl::word::Priority; + template + using Priority = dsl::word::Priority; /// @copydoc dsl::word::Always using Always = dsl::word::Always; diff --git a/src/dsl/Parse.hpp b/src/dsl/Parse.hpp index 1e38116db..7d923b911 100644 --- a/src/dsl/Parse.hpp +++ b/src/dsl/Parse.hpp @@ -76,7 +76,7 @@ namespace dsl { Parse>(task); } - static int priority(threading::ReactionTask& task) { + static util::Priority priority(threading::ReactionTask& task) { return std::conditional_t::value, DSL, fusion::NoOp>::template priority< Parse>(task); } diff --git a/src/dsl/fusion/NoOp.hpp b/src/dsl/fusion/NoOp.hpp index 5b20e5ee0..47576f33d 100644 --- a/src/dsl/fusion/NoOp.hpp +++ b/src/dsl/fusion/NoOp.hpp @@ -80,8 +80,8 @@ namespace dsl { } template - static int priority(const threading::ReactionTask& /*task*/) { - return word::Priority::NORMAL::value; + static util::Priority priority(const threading::ReactionTask& /*task*/) { + return util::Priority::NORMAL; } template diff --git a/src/dsl/fusion/PriorityFusion.hpp b/src/dsl/fusion/PriorityFusion.hpp index afd8b5175..34a6dd70d 100644 --- a/src/dsl/fusion/PriorityFusion.hpp +++ b/src/dsl/fusion/PriorityFusion.hpp @@ -44,7 +44,7 @@ namespace dsl { struct PriorityFuser> { template - static int priority(threading::ReactionTask& task) { + static util::Priority priority(threading::ReactionTask& task) { // Return our priority return Word::template priority(task); @@ -56,7 +56,7 @@ namespace dsl { struct PriorityFuser> { template - static int priority(threading::ReactionTask& task) { + static util::Priority priority(threading::ReactionTask& task) { // Choose our maximum priority return std::max(Word1::template priority(task), diff --git a/src/dsl/word/Always.hpp b/src/dsl/word/Always.hpp index 9292f7395..53310effd 100644 --- a/src/dsl/word/Always.hpp +++ b/src/dsl/word/Always.hpp @@ -131,7 +131,7 @@ namespace dsl { auto idle_task = std::make_unique( reaction, false, - [](threading::ReactionTask& task) { return DSL::priority(task) - 1; }, + [](threading::ReactionTask& task) { return prev(DSL::priority(task)); }, DSL::run_inline, DSL::pool, DSL::group); diff --git a/src/dsl/word/Priority.hpp b/src/dsl/word/Priority.hpp index 38e5d9540..7b10853a7 100644 --- a/src/dsl/word/Priority.hpp +++ b/src/dsl/word/Priority.hpp @@ -24,11 +24,11 @@ #define NUCLEAR_DSL_WORD_PRIORITY_HPP #include "../../threading/Reaction.hpp" +#include "../../util/Priority.hpp" namespace NUClear { namespace dsl { namespace word { - /** * Task priority can be controlled using an assigned setting. * @@ -67,57 +67,12 @@ namespace dsl { * @par Implements * Fusion */ + template struct Priority { - - struct REALTIME { - /// Realtime priority runs with 1000 value - static constexpr int value = 1000; - - template - static int priority(const threading::ReactionTask& /*task*/) { - return value; - } - }; - - struct HIGH { - /// High priority runs with 750 value - static constexpr int value = 750; - - template - static int priority(const threading::ReactionTask& /*task*/) { - return value; - } - }; - - struct NORMAL { - /// Normal priority runs with 500 value - static constexpr int value = 500; - - template - static int priority(const threading::ReactionTask& /*task*/) { - return value; - } - }; - - struct LOW { - /// Low priority runs with 250 value - static constexpr int value = 250; - - template - static int priority(const threading::ReactionTask& /*task*/) { - return value; - } - }; - - struct IDLE { - /// Idle tasks run with 0 priority, they run when there is free time - static constexpr int value = 0; - - template - static int priority(const threading::ReactionTask& /*task*/) { - return value; - } - }; + template + static util::Priority priority(const threading::ReactionTask& /*task*/) { + return value; + } }; } // namespace word diff --git a/src/dsl/word/Shutdown.hpp b/src/dsl/word/Shutdown.hpp index 20ea2c160..90f0b9d1e 100644 --- a/src/dsl/word/Shutdown.hpp +++ b/src/dsl/word/Shutdown.hpp @@ -24,6 +24,7 @@ #define NUCLEAR_DSL_WORD_SHUTDOWN_HPP #include "../operation/TypeBind.hpp" +#include "../../util/Priority.hpp" namespace NUClear { namespace dsl { @@ -54,7 +55,7 @@ namespace dsl { */ struct Shutdown : operation::TypeBind - , Priority::IDLE {}; + , Priority {}; } // namespace word } // namespace dsl diff --git a/src/extension/ChronoController.cpp b/src/extension/ChronoController.cpp index f7c01bf36..3c97cc921 100644 --- a/src/extension/ChronoController.cpp +++ b/src/extension/ChronoController.cpp @@ -121,7 +121,7 @@ namespace extension { wait.notify_all(); }); - on().then("Chrono Controller", [this] { + on>().then("Chrono Controller", [this] { // Run until we are told to stop while (running.load(std::memory_order_acquire)) { diff --git a/src/threading/ReactionTask.hpp b/src/threading/ReactionTask.hpp index 10979681b..c0fe54e62 100644 --- a/src/threading/ReactionTask.hpp +++ b/src/threading/ReactionTask.hpp @@ -32,6 +32,7 @@ #include "../util/GroupDescriptor.hpp" #include "../util/Inline.hpp" #include "../util/ThreadPoolDescriptor.hpp" +#include "../util/Priority.hpp" #include "../util/platform.hpp" #include "Reaction.hpp" @@ -136,7 +137,7 @@ namespace threading { bool run_inline{false}; /// The priority to run this task at - int priority; + util::Priority priority; /// If the task should be executed inline (in the current thread) or not util::Inline should_inline{util::Inline::NEUTRAL}; /// Details about the thread pool that this task will run from, this will also influence what task queue diff --git a/src/threading/scheduler/Group.cpp b/src/threading/scheduler/Group.cpp index 883180c30..d4cfb1e07 100644 --- a/src/threading/scheduler/Group.cpp +++ b/src/threading/scheduler/Group.cpp @@ -33,7 +33,7 @@ namespace NUClear { namespace threading { namespace scheduler { - Group::LockHandle::LockHandle(const NUClear::id_t& task_id, const int& priority, std::function notify) + Group::LockHandle::LockHandle(const NUClear::id_t& task_id, const util::Priority& priority, std::function notify) : task_id(task_id), priority(priority), notify(std::move(notify)) {} Group::GroupLock::GroupLock(Group& group, std::shared_ptr handle) @@ -109,7 +109,7 @@ namespace threading { Group::Group(std::shared_ptr descriptor) : descriptor(std::move(descriptor)) {} std::unique_ptr Group::lock(const NUClear::id_t& task_id, - const int& priority, + const util::Priority& priority, const std::function& notify) { auto handle = std::make_shared(task_id, priority, notify); diff --git a/src/threading/scheduler/Group.hpp b/src/threading/scheduler/Group.hpp index 785b9da85..5a7a0059a 100644 --- a/src/threading/scheduler/Group.hpp +++ b/src/threading/scheduler/Group.hpp @@ -28,6 +28,7 @@ #include #include "../../util/GroupDescriptor.hpp" +#include "../../util/Priority.hpp" #include "Lock.hpp" namespace NUClear { @@ -50,7 +51,7 @@ namespace threading { * It holds if the lock should currently be locked, as well as ordering which locks should be locked first. */ struct LockHandle { - LockHandle(const NUClear::id_t& task_id, const int& priority, std::function notify); + LockHandle(const NUClear::id_t& task_id, const util::Priority& priority, std::function notify); /** * Compare two lock handles by comparing their priority and task id @@ -78,7 +79,7 @@ namespace threading { /// The task id of the reaction that is waiting, lower task ids run first NUClear::id_t task_id; /// The priority of the reaction that is waiting, higher priorities run first - int priority; + util::Priority priority; /// If this lock has been successfully locked bool locked{false}; /// If this lock has been notified that it can lock @@ -156,7 +157,7 @@ namespace threading { * @return a lock which can be locked once a token is available */ std::unique_ptr lock(const NUClear::id_t& task_id, - const int& priority, + const util::Priority& priority, const std::function& notify); /// The descriptor for this group diff --git a/src/threading/scheduler/Pool.cpp b/src/threading/scheduler/Pool.cpp index a9b6cb97a..611a6354b 100644 --- a/src/threading/scheduler/Pool.cpp +++ b/src/threading/scheduler/Pool.cpp @@ -257,7 +257,7 @@ namespace threading { auto task = std::make_unique( nullptr, true, - [](const ReactionTask&) { return 0; }, + [](const ReactionTask&) { return util::Priority::LOWEST; }, [](const ReactionTask&) { return util::Inline::ALWAYS; }, [](const ReactionTask&) { return dsl::word::Pool<>::descriptor(); }, [](const ReactionTask&) { return std::set>{}; }); diff --git a/src/threading/scheduler/Scheduler.cpp b/src/threading/scheduler/Scheduler.cpp index cf1c4e002..c178d50cf 100644 --- a/src/threading/scheduler/Scheduler.cpp +++ b/src/threading/scheduler/Scheduler.cpp @@ -150,7 +150,7 @@ namespace threading { std::unique_ptr Scheduler::get_groups_lock( const NUClear::id_t& task_id, - const int& priority, + const util::Priority& priority, const std::shared_ptr& pool, const std::set>& descs) { diff --git a/src/threading/scheduler/Scheduler.hpp b/src/threading/scheduler/Scheduler.hpp index 0c30970a6..651ca2157 100644 --- a/src/threading/scheduler/Scheduler.hpp +++ b/src/threading/scheduler/Scheduler.hpp @@ -126,7 +126,7 @@ namespace threading { * @return a combined lock representing the state of all the groups */ std::unique_ptr get_groups_lock(const NUClear::id_t& task_id, - const int& priority, + const util::Priority& priority, const std::shared_ptr& pool, const std::set>& descs); diff --git a/src/util/CallbackGenerator.hpp b/src/util/CallbackGenerator.hpp index 93ada18c1..30530ae46 100644 --- a/src/util/CallbackGenerator.hpp +++ b/src/util/CallbackGenerator.hpp @@ -29,6 +29,7 @@ #include "../message/ReactionStatistics.hpp" #include "../util/MergeTransient.hpp" #include "../util/TransientDataElements.hpp" +#include "../util/Priority.hpp" #include "../util/apply.hpp" #include "../util/unpack.hpp" #include "../util/update_current_thread_priority.hpp" diff --git a/src/util/Priority.hpp b/src/util/Priority.hpp new file mode 100644 index 000000000..32348aa8b --- /dev/null +++ b/src/util/Priority.hpp @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2024 NUClear Contributors + * + * This file is part of the NUClear codebase. + * See https://github.com/Fastcode/NUClear for further info. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef NUCLEAR_UTIL_PRIORITY_HPP +#define NUCLEAR_UTIL_PRIORITY_HPP + +#include +#include +#include + +namespace NUClear { +namespace util { + + enum class Priority : uint8_t { LOWEST = 0, LOW = 1, NORMAL = 2, HIGH = 3, HIGHEST = 4 }; + inline Priority prev(Priority value) { + value = static_cast(static_cast>(value) - 1); + value = std::min(value, Priority::LOWEST); + return value; + } + constexpr Priority MAX_PRIORITY = Priority::HIGHEST; + +} // namespace util +} // namespace NUClear + +#endif // NUCLEAR_UTIL_PRIORITY_HPP diff --git a/src/util/update_current_thread_priority.hpp b/src/util/update_current_thread_priority.hpp index dbdb09990..1aa6e52d8 100644 --- a/src/util/update_current_thread_priority.hpp +++ b/src/util/update_current_thread_priority.hpp @@ -23,18 +23,18 @@ #ifndef NUCLEAR_UTIL_UPDATE_CURRENT_THREAD_PRIORITY_HPP #define NUCLEAR_UTIL_UPDATE_CURRENT_THREAD_PRIORITY_HPP +#include "../util/Priority.hpp" + #ifndef _WIN32 #include -inline void update_current_thread_priority(int priority) { - - // TODO(Trent) SCHED_NORMAL for normal threads - // TODO(Trent) SCHED_FIFO for realtime threads - // TODO(Trent) SCHED_RR for high priority threads +inline void update_current_thread_priority(NUClear::util::Priority priority) { + auto priority_int = static_cast>(priority); - auto sched_priority = sched_get_priority_min(SCHED_RR) - + (priority / (sched_get_priority_max(SCHED_RR) - sched_get_priority_min(SCHED_RR))); + auto step = (sched_get_priority_max(SCHED_RR) - sched_get_priority_min(SCHED_RR)) + / static_cast>(NUClear::util::MAX_PRIORITY); + auto sched_priority = priority_int * step; sched_param p{}; p.sched_priority = sched_priority; @@ -46,30 +46,24 @@ inline void update_current_thread_priority(int priority) { #include "platform.hpp" -inline void update_current_thread_priority(int priority) { +inline void update_current_thread_priority(NUClear::util::Priority priority) { - switch ((priority * 7) / 1000) { - case 0: { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); - } break; - case 1: { + switch (priority) { + case LOWEST: { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); } break; - case 2: { + case LOW: { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); } break; - case 3: { + case NORMAL: { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); } break; - case 4: { + case HIGH: { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); } break; - case 5: { + case HIGHEST: { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); } break; - case 6: { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - } break; } } From 016dce31c153a7098440931481956b8287c0665f Mon Sep 17 00:00:00 2001 From: cameron Date: Sun, 15 Dec 2024 17:41:53 +1100 Subject: [PATCH 02/73] add documentation to Priority DSL and util --- src/dsl/word/Priority.hpp | 20 ++++---------------- src/util/Priority.hpp | 7 ++++++- src/util/update_current_thread_priority.hpp | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/dsl/word/Priority.hpp b/src/dsl/word/Priority.hpp index 7b10853a7..1c6ee7154 100644 --- a/src/dsl/word/Priority.hpp +++ b/src/dsl/word/Priority.hpp @@ -32,36 +32,24 @@ namespace dsl { /** * Task priority can be controlled using an assigned setting. * - * @code on, Priority::HIGH>() @endcode + * @code on, Priority>() @endcode * The PowerPlant uses this setting to determine the scheduling order in the threadpool, as well as assign a * priority to the thread on the OS. * * The available priority settings are: * - * REALTIME: Tasks assigned with this will be queued with all other REALTIME tasks. - * + * HIGHEST: * HIGH: - * Tasks assigned with this will be queued with all other HIGH tasks. - * They will be scheduled for execution when there are no REALTIME tasks in the queue. - * * NORMAL: - * Tasks assigned with this will be queued with all other NORMAL tasks. - * They will be scheduled for execution when there are no REALTIME and HIGH tasks in the queue. - * * LOW: - * Tasks assigned with this will be queued with all other LOW tasks. - * They will be scheduled for execution when there are no REALTIME, HIGH and NORMAL tasks in the queue. - * - * IDLE: - * Tasks assigned with this priority will be queued with all other IDLE tasks. - * They will be scheduled for execution when there are no other tasks running in the system. + * LOWEST: * * @par Default Behaviour * @code on>() @endcode * When the priority is not specified, tasks will be assigned a default setting; NORMAL. * * @attention - * If the OS allows the user to set thread priority, this word can also be used to assign the priority of the + * If the OS allows the user to set thread priority, this word will also be used to assign the priority of the * thread in its runtime environment. * * @par Implements diff --git a/src/util/Priority.hpp b/src/util/Priority.hpp index 32348aa8b..6e5781810 100644 --- a/src/util/Priority.hpp +++ b/src/util/Priority.hpp @@ -30,13 +30,18 @@ namespace NUClear { namespace util { + /** + * The priority value for Priority dsl word + */ enum class Priority : uint8_t { LOWEST = 0, LOW = 1, NORMAL = 2, HIGH = 3, HIGHEST = 4 }; + /** + * Gets one lower priority unless already lowest + */ inline Priority prev(Priority value) { value = static_cast(static_cast>(value) - 1); value = std::min(value, Priority::LOWEST); return value; } - constexpr Priority MAX_PRIORITY = Priority::HIGHEST; } // namespace util } // namespace NUClear diff --git a/src/util/update_current_thread_priority.hpp b/src/util/update_current_thread_priority.hpp index 1aa6e52d8..940d86a39 100644 --- a/src/util/update_current_thread_priority.hpp +++ b/src/util/update_current_thread_priority.hpp @@ -33,7 +33,7 @@ inline void update_current_thread_priority(NUClear::util::Priority priority) { auto priority_int = static_cast>(priority); auto step = (sched_get_priority_max(SCHED_RR) - sched_get_priority_min(SCHED_RR)) - / static_cast>(NUClear::util::MAX_PRIORITY); + / static_cast>(NUClear::util::Priority::HIGHEST); auto sched_priority = priority_int * step; sched_param p{}; From 2dba179205fc4083ceb5e1079afab7ee3293dbf7 Mon Sep 17 00:00:00 2001 From: cameron Date: Sun, 15 Dec 2024 18:36:09 +1100 Subject: [PATCH 03/73] fix tests --- src/dsl/word/emit/Initialise.hpp | 2 +- tests/tests/api/ReactionHandle.cpp | 4 +- tests/tests/api/ReactionStatistics.cpp | 4 +- tests/tests/api/ReactionStatisticsTiming.cpp | 4 +- tests/tests/dsl/BlockNoData.cpp | 2 +- tests/tests/dsl/Buffer.cpp | 10 ++-- tests/tests/dsl/DSLOrdering.cpp | 6 +-- tests/tests/dsl/FlagMessage.cpp | 2 +- tests/tests/dsl/IdleGlobal.cpp | 2 +- tests/tests/dsl/IdleSingle.cpp | 4 +- tests/tests/dsl/Once.cpp | 2 +- tests/tests/dsl/Priority.cpp | 44 ++++++++--------- tests/tests/dsl/RawFunction.cpp | 8 +-- tests/tests/dsl/Shutdown.cpp | 2 +- tests/tests/dsl/Transient.cpp | 18 +++---- tests/tests/dsl/With.cpp | 10 ++-- tests/tests/threading/Group.cpp | 52 ++++++++++---------- 17 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/dsl/word/emit/Initialise.hpp b/src/dsl/word/emit/Initialise.hpp index cd013fdd8..435d808a0 100644 --- a/src/dsl/word/emit/Initialise.hpp +++ b/src/dsl/word/emit/Initialise.hpp @@ -59,7 +59,7 @@ namespace dsl { auto emitter = std::make_unique( nullptr, false, - [](threading::ReactionTask& /*task*/) { return 1000; }, + [](threading::ReactionTask& /*task*/) { return NUClear::util::Priority::HIGHEST; }, [](threading::ReactionTask& /*task*/) { return util::Inline::NEVER; }, [](threading::ReactionTask& /*task*/) { return Pool<>::descriptor(); }, [](threading::ReactionTask& /*task*/) { diff --git a/tests/tests/api/ReactionHandle.cpp b/tests/tests/api/ReactionHandle.cpp index 427ecf014..794a32c2d 100644 --- a/tests/tests/api/ReactionHandle.cpp +++ b/tests/tests/api/ReactionHandle.cpp @@ -37,13 +37,13 @@ class TestReactor : public test_util::TestBase { TestReactor(std::unique_ptr environment) : TestBase(std::move(environment)) { // Make an always disabled reaction - a = on, Priority::HIGH>().then([this](const Message& msg) { // + a = on, Priority>().then([this](const Message& msg) { // events.push_back("Executed disabled reaction " + std::to_string(msg.i)); }); a.disable(); // Make a reaction that we toggle on and off - b = on, Priority::HIGH>().then([this](const Message& msg) { // + b = on, Priority>().then([this](const Message& msg) { // events.push_back("Executed toggled reaction " + std::to_string(msg.i)); b.disable(); emit(std::make_unique(1)); diff --git a/tests/tests/api/ReactionStatistics.cpp b/tests/tests/api/ReactionStatistics.cpp index 9d8d412d9..54dc989f0 100644 --- a/tests/tests/api/ReactionStatistics.cpp +++ b/tests/tests/api/ReactionStatistics.cpp @@ -40,13 +40,13 @@ class TestReactor : public test_util::TestBase { // This reaction is here to emit something from a ReactionStatistics trigger // This shouldn't cause reaction statistics of their own otherwise everything would explode - on, Priority::HIGH>().then("Loop Statistics", [this](const ReactionEvent&) { // + on, Priority>().then("Loop Statistics", [this](const ReactionEvent&) { // emit(std::make_unique()); }); on>().then("No Statistics", [] {}); - on, Priority::HIGH>().then("Reaction Stats Handler", [this](const ReactionEvent& event) { + on, Priority>().then("Reaction Stats Handler", [this](const ReactionEvent& event) { const auto& stats = *event.statistics; // Other reactions statistics run on this because of built in NUClear reactors (e.g. chrono controller etc) diff --git a/tests/tests/api/ReactionStatisticsTiming.cpp b/tests/tests/api/ReactionStatisticsTiming.cpp index f0ee99045..a35065bab 100644 --- a/tests/tests/api/ReactionStatisticsTiming.cpp +++ b/tests/tests/api/ReactionStatisticsTiming.cpp @@ -60,7 +60,7 @@ class TestReactor : public test_util::TestBase { TestReactor(std::unique_ptr environment) : TestBase(std::move(environment), true, std::chrono::seconds(2)) { - on>, Priority::LOW>().then(initial_name + ":" + heavy_name, [this] { + on>, Priority>().then(initial_name + ":" + heavy_name, [this] { code_events.emplace_back("Started " + initial_name + ":" + heavy_name, NUClear::clock::now()); code_events.emplace_back("Created " + heavy_name, NUClear::clock::now()); emit(std::make_unique()); @@ -74,7 +74,7 @@ class TestReactor : public test_util::TestBase { code_events.emplace_back("Finished " + heavy_name, NUClear::clock::now()); }); - on>, Priority::LOW>().then(initial_name + ":" + light_name, [this] { + on>, Priority>().then(initial_name + ":" + light_name, [this] { code_events.emplace_back("Started " + initial_name + ":" + light_name, NUClear::clock::now()); code_events.emplace_back("Created " + light_name, NUClear::clock::now()); emit(std::make_unique()); diff --git a/tests/tests/dsl/BlockNoData.cpp b/tests/tests/dsl/BlockNoData.cpp index 702728ac4..c9d8294ef 100644 --- a/tests/tests/dsl/BlockNoData.cpp +++ b/tests/tests/dsl/BlockNoData.cpp @@ -47,7 +47,7 @@ class TestReactor : public test_util::TestBase { events.push_back("MessageB with MessageA triggered"); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting MessageA"); emit(std::make_unique()); }); diff --git a/tests/tests/dsl/Buffer.cpp b/tests/tests/dsl/Buffer.cpp index 5e465ef09..14e38a352 100644 --- a/tests/tests/dsl/Buffer.cpp +++ b/tests/tests/dsl/Buffer.cpp @@ -51,29 +51,29 @@ class TestReactor : public test_util::TestBase { events.push_back("Buffer<4> reaction " + std::to_string(msg.i)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step 1"); emit(std::make_unique(1)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step 2"); emit(std::make_unique(2)); emit(std::make_unique(3)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step 3"); emit(std::make_unique(4)); emit(std::make_unique(5)); emit(std::make_unique(6)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step 4"); emit(std::make_unique(7)); emit(std::make_unique(8)); emit(std::make_unique(9)); emit(std::make_unique(10)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step 5"); emit(std::make_unique(11)); emit(std::make_unique(12)); diff --git a/tests/tests/dsl/DSLOrdering.cpp b/tests/tests/dsl/DSLOrdering.cpp index 7deea04f1..aee901b98 100644 --- a/tests/tests/dsl/DSLOrdering.cpp +++ b/tests/tests/dsl/DSLOrdering.cpp @@ -47,15 +47,15 @@ class TestReactor : public test_util::TestBase { events.push_back("Empty function"); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting 1"); emit(std::make_unique>("1")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting 2"); emit(std::make_unique>("2")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting 3"); emit(std::make_unique>("3")); }); diff --git a/tests/tests/dsl/FlagMessage.cpp b/tests/tests/dsl/FlagMessage.cpp index 33b81b19a..817b367c4 100644 --- a/tests/tests/dsl/FlagMessage.cpp +++ b/tests/tests/dsl/FlagMessage.cpp @@ -50,7 +50,7 @@ class TestReactor : public test_util::TestBase { events.push_back("MessageA with MessageB triggered"); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Step<1> triggered"); events.push_back("Emitting MessageA"); emit(std::make_unique()); diff --git a/tests/tests/dsl/IdleGlobal.cpp b/tests/tests/dsl/IdleGlobal.cpp index 47d280ffb..2b012f846 100644 --- a/tests/tests/dsl/IdleGlobal.cpp +++ b/tests/tests/dsl/IdleGlobal.cpp @@ -89,7 +89,7 @@ class TestReactor : public test_util::TestBase { }); // At low priority shutdown, this will run after all the global idles (should be 1) have been fired - on>, Priority::LOW>().then([this] { powerplant.shutdown(); }); + on>, Priority>().then([this] { powerplant.shutdown(); }); // This shutdown is here in case the test times out so all the spinlocks don't hang the test on>().then([this] { diff --git a/tests/tests/dsl/IdleSingle.cpp b/tests/tests/dsl/IdleSingle.cpp index 27459cf4d..17c44de85 100644 --- a/tests/tests/dsl/IdleSingle.cpp +++ b/tests/tests/dsl/IdleSingle.cpp @@ -76,7 +76,7 @@ class TestReactor : public test_util::TestBase { // Run this at low priority but have it first // This way MainThread will get notified that it has access to Sync but then it will lose it when // The other task on the default pool gets created so it'll be notified but unable to act - on, MainThread, Priority::LOW, Sync>().then([this](const TaskB& t) { + on, MainThread, Priority, Sync>().then([this](const TaskB& t) { main_calls[t.i].fetch_add(1, std::memory_order_relaxed); if (t.i + 1 < n_loops) { @@ -87,7 +87,7 @@ class TestReactor : public test_util::TestBase { } }); // This is the high priority task that preempts the main thread and makes it go idle again - on, Pool<>, Priority::HIGH, Sync>().then([this](const TaskB& t) { // + on, Pool<>, Priority, Sync>().then([this](const TaskB& t) { // default_calls[t.i].fetch_add(1, std::memory_order_relaxed); }); diff --git a/tests/tests/dsl/Once.cpp b/tests/tests/dsl/Once.cpp index d8bff7e4d..f482c3f18 100644 --- a/tests/tests/dsl/Once.cpp +++ b/tests/tests/dsl/Once.cpp @@ -36,7 +36,7 @@ class TestReactor : public NUClear::Reactor { TestReactor(std::unique_ptr environment) : Reactor(std::move(environment)) { // Make this priority high so it will always run first if it is able - on, Priority::HIGH, Once>().then([this](const SimpleMessage& msg) { // + on, Priority, Once>().then([this](const SimpleMessage& msg) { // events.push_back("Once Trigger executed " + std::to_string(msg.run)); }); diff --git a/tests/tests/dsl/Priority.cpp b/tests/tests/dsl/Priority.cpp index ae650f738..3404ff0bd 100644 --- a/tests/tests/dsl/Priority.cpp +++ b/tests/tests/dsl/Priority.cpp @@ -35,20 +35,20 @@ class TestReactor : public test_util::TestBase { TestReactor(std::unique_ptr environment) : TestBase(std::move(environment)) { // Declare in the order you'd expect them to fire - on>, Priority::REALTIME>().then([this] { events.push_back("Realtime Message<1>"); }); - on>, Priority::HIGH>().then("High", [this] { events.push_back("High Message<1>"); }); + on>, Priority>().then([this] { events.push_back("Highest Message<1>"); }); + on>, Priority>().then("High", [this] { events.push_back("High Message<1>"); }); on>>().then([this] { events.push_back("Default Message<1>"); }); - on>, Priority::NORMAL>().then("Normal", [this] { events.push_back("Normal Message<1>"); }); - on>, Priority::LOW>().then("Low", [this] { events.push_back("Low Message<1>"); }); - on>, Priority::IDLE>().then([this] { events.push_back("Idle Message<1>"); }); + on>, Priority>().then("Normal", [this] { events.push_back("Normal Message<1>"); }); + on>, Priority>().then("Low", [this] { events.push_back("Low Message<1>"); }); + on>, Priority>().then([this] { events.push_back("Lowest Message<1>"); }); // Declare in the opposite order to what you'd expect them to fire - on>, Priority::IDLE>().then([this] { events.push_back("Idle Message<2>"); }); - on>, Priority::LOW>().then([this] { events.push_back("Low Message<2>"); }); - on>, Priority::NORMAL>().then([this] { events.push_back("Normal Message<2>"); }); + on>, Priority>().then([this] { events.push_back("Lowest Message<2>"); }); + on>, Priority>().then([this] { events.push_back("Low Message<2>"); }); + on>, Priority>().then([this] { events.push_back("Normal Message<2>"); }); on>>().then([this] { events.push_back("Default Message<2>"); }); - on>, Priority::HIGH>().then([this] { events.push_back("High Message<2>"); }); - on>, Priority::REALTIME>().then([this] { events.push_back("Realtime Message<2>"); }); + on>, Priority>().then([this] { events.push_back("High Message<2>"); }); + on>, Priority>().then([this] { events.push_back("Highest Message<2>"); }); // Declare in a random order std::array order = {0, 1, 2, 3, 4}; @@ -56,21 +56,21 @@ class TestReactor : public test_util::TestBase { for (const auto& i : order) { switch (i) { case 0: - on>, Priority::REALTIME>().then( - [this] { events.push_back("Realtime Message<3>"); }); + on>, Priority>().then( + [this] { events.push_back("Highest Message<3>"); }); break; case 1: - on>, Priority::HIGH>().then([this] { events.push_back("High Message<3>"); }); + on>, Priority>().then([this] { events.push_back("High Message<3>"); }); break; case 2: - on>, Priority::NORMAL>().then([this] { events.push_back("Normal Message<3>"); }); + on>, Priority>().then([this] { events.push_back("Normal Message<3>"); }); on>>().then([this] { events.push_back("Default Message<3>"); }); break; case 3: - on>, Priority::LOW>().then([this] { events.push_back("Low Message<3>"); }); + on>, Priority>().then([this] { events.push_back("Low Message<3>"); }); break; case 4: - on>, Priority::IDLE>().then([this] { events.push_back("Idle Message<3>"); }); + on>, Priority>().then([this] { events.push_back("Lowest Message<3>"); }); break; default: throw std::invalid_argument("Should be impossible"); } @@ -98,9 +98,9 @@ TEST_CASE("Tests that priority orders the tasks appropriately", "[api][priority] plant.start(); const std::vector expected = { - "Realtime Message<1>", - "Realtime Message<2>", - "Realtime Message<3>", + "Highest Message<1>", + "Highest Message<2>", + "Highest Message<3>", "High Message<1>", "High Message<2>", "High Message<3>", @@ -113,9 +113,9 @@ TEST_CASE("Tests that priority orders the tasks appropriately", "[api][priority] "Low Message<1>", "Low Message<2>", "Low Message<3>", - "Idle Message<1>", - "Idle Message<2>", - "Idle Message<3>", + "Lowest Message<1>", + "Lowest Message<2>", + "Lowest Message<3>", }; // Make an info print the diff in an easy to read way if we fail diff --git a/tests/tests/dsl/RawFunction.cpp b/tests/tests/dsl/RawFunction.cpp index b7d7c7dda..7750c70ad 100644 --- a/tests/tests/dsl/RawFunction.cpp +++ b/tests/tests/dsl/RawFunction.cpp @@ -88,10 +88,10 @@ class TestReactor : public test_util::TestBase { on, Trigger>().then(raw_function_test_right_arg); on, Trigger>().then(raw_function_test_both_args); - on>, Priority::LOW>().then([this] { emit(std::make_unique("D1")); }); - on>, Priority::LOW>().then([this] { emit(std::make_unique("M2")); }); - on>, Priority::LOW>().then([this] { emit(std::make_unique("D3")); }); - on>, Priority::LOW>().then([this] { emit(std::make_unique("M4")); }); + on>, Priority>().then([this] { emit(std::make_unique("D1")); }); + on>, Priority>().then([this] { emit(std::make_unique("M2")); }); + on>, Priority>().then([this] { emit(std::make_unique("D3")); }); + on>, Priority>().then([this] { emit(std::make_unique("M4")); }); on().then([this] { emit(std::make_unique>()); diff --git a/tests/tests/dsl/Shutdown.cpp b/tests/tests/dsl/Shutdown.cpp index 9f644217d..5741092a7 100644 --- a/tests/tests/dsl/Shutdown.cpp +++ b/tests/tests/dsl/Shutdown.cpp @@ -34,7 +34,7 @@ class TestReactor : public test_util::TestBase { events.push_back("Shutdown task executed"); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Requesting shutdown"); powerplant.shutdown(); }); diff --git a/tests/tests/dsl/Transient.cpp b/tests/tests/dsl/Transient.cpp index dd9574060..d36a135b7 100644 --- a/tests/tests/dsl/Transient.cpp +++ b/tests/tests/dsl/Transient.cpp @@ -72,39 +72,39 @@ class TestReactor : public test_util::TestBase { events.push_back(m.msg + " : " + t.msg); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 1"); emit(std::make_unique("S1")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Transient 1"); emit(std::make_unique("T1", true)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 2"); emit(std::make_unique("S2")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Invalid Transient 2"); emit(std::make_unique("T2", false)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 3"); emit(std::make_unique("S3")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Transient 3"); emit(std::make_unique("T3", true)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Transient 4"); emit(std::make_unique("T4", true)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Invalid Transient 5"); emit(std::make_unique("T5", false)); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 4"); emit(std::make_unique("S4")); }); diff --git a/tests/tests/dsl/With.cpp b/tests/tests/dsl/With.cpp index fd389bcc3..35edac64f 100644 --- a/tests/tests/dsl/With.cpp +++ b/tests/tests/dsl/With.cpp @@ -43,27 +43,27 @@ class TestReactor : public test_util::TestBase { events.push_back("Message: " + m.data + " Data: " + d.data); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Data 1"); emit(std::make_unique("D1")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Data 2"); emit(std::make_unique("D2")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 1"); emit(std::make_unique("M1")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Data 3"); emit(std::make_unique("D3")); }); - on>, Priority::LOW>().then([this] { + on>, Priority>().then([this] { events.push_back("Emitting Message 2"); emit(std::make_unique("M2")); }); diff --git a/tests/tests/threading/Group.cpp b/tests/tests/threading/Group.cpp index ea94b6834..ecbb52c89 100644 --- a/tests/tests/threading/Group.cpp +++ b/tests/tests/threading/Group.cpp @@ -42,14 +42,14 @@ namespace threading { NUClear::id_t task_id_source = 1; WHEN("Creating a lock") { - std::unique_ptr lock1 = group->lock(++task_id_source, 1, [] {}); + std::unique_ptr lock1 = group->lock(++task_id_source, util::Priority::LOW, [] {}); THEN("The lock should be true") { CHECK(lock1->lock() == true); } AND_WHEN("Creating a second lock") { - std::unique_ptr lock2 = group->lock(++task_id_source, 1, [] {}); + std::unique_ptr lock2 = group->lock(++task_id_source, util::Priority::LOW, [] {}); THEN("The lock should be false") { CHECK(lock1->lock() == true); @@ -67,7 +67,7 @@ namespace threading { WHEN("Creating a lock and locking it") { int notified1 = 0; - std::unique_ptr lock1 = group->lock(task_id_source++, 1, [&] { ++notified1; }); + std::unique_ptr lock1 = group->lock(task_id_source++, util::Priority::LOW, [&] { ++notified1; }); lock1->lock(); THEN("The lock should be true") { @@ -77,8 +77,8 @@ namespace threading { AND_WHEN("Creating two more locks") { int notified2 = 0; int notified3 = 0; - std::unique_ptr lock2 = group->lock(task_id_source++, 1, [&] { ++notified2; }); - std::unique_ptr lock3 = group->lock(task_id_source++, 1, [&] { ++notified3; }); + std::unique_ptr lock2 = group->lock(task_id_source++, util::Priority::LOW, [&] { ++notified2; }); + std::unique_ptr lock3 = group->lock(task_id_source++, util::Priority::LOW, [&] { ++notified3; }); THEN("The new locks should be false") { CHECK(lock1->lock() == true); @@ -116,11 +116,11 @@ namespace threading { WHEN("Creating a lock and locking it") { int notified1 = 0; - std::unique_ptr lock1 = group->lock(1, 1, [&] { ++notified1; }); + std::unique_ptr lock1 = group->lock(1, util::Priority::LOW, [&] { ++notified1; }); AND_WHEN("Locking the lock and creating a higher priority task") { lock1->lock(); - std::unique_ptr lock2 = group->lock(2, 2, [] {}); + std::unique_ptr lock2 = group->lock(2, util::Priority::NORMAL, [] {}); THEN("The new lock should be false") { CHECK(lock1->lock() == true); @@ -128,7 +128,7 @@ namespace threading { } } AND_WHEN("Not locking the lock and creating a higher priority task") { - std::unique_ptr lock2 = group->lock(2, 2, [] {}); + std::unique_ptr lock2 = group->lock(2, util::Priority::NORMAL, [] {}); THEN("The new lock should be true") { CHECK(lock1->lock() == false); @@ -151,11 +151,11 @@ namespace threading { std::array notified = {0, 0, 0, 0, 0}; std::array, n_locks> locks; - locks[3] = group->lock(3, 1, [&] { ++notified[3]; }); - locks[1] = group->lock(1, 1, [&] { ++notified[1]; }); - locks[4] = group->lock(4, 1, [&] { ++notified[4]; }); - locks[0] = group->lock(0, 1, [&] { ++notified[0]; }); - locks[2] = group->lock(2, 1, [&] { ++notified[2]; }); + locks[3] = group->lock(3, util::Priority::LOW, [&] { ++notified[3]; }); + locks[1] = group->lock(1, util::Priority::LOW, [&] { ++notified[1]; }); + locks[4] = group->lock(4, util::Priority::LOW, [&] { ++notified[4]; }); + locks[0] = group->lock(0, util::Priority::LOW, [&] { ++notified[0]; }); + locks[2] = group->lock(2, util::Priority::LOW, [&] { ++notified[2]; }); THEN("The locks should be lockable in the proper order") { CHECK(locks[0]->lock() == (0 < n_tokens)); @@ -198,11 +198,11 @@ namespace threading { WHEN("Creating a series of locks") { std::array notified = {0, 0, 0, 0, 0}; std::array, n_locks> locks = { - group->lock(0, 1, [&] { ++notified[0]; }), - group->lock(1, 1, [&] { ++notified[1]; }), - group->lock(2, 1, [&] { ++notified[2]; }), - group->lock(3, 1, [&] { ++notified[3]; }), - group->lock(4, 1, [&] { ++notified[4]; }), + group->lock(0, util::Priority::LOW, [&] { ++notified[0]; }), + group->lock(1, util::Priority::LOW, [&] { ++notified[1]; }), + group->lock(2, util::Priority::LOW, [&] { ++notified[2]; }), + group->lock(3, util::Priority::LOW, [&] { ++notified[3]; }), + group->lock(4, util::Priority::LOW, [&] { ++notified[4]; }), }; // Note that because this is in a scope, for the rest of the AND_WHEN calls, no locks have been @@ -291,9 +291,9 @@ namespace threading { WHEN("Creating a series of locks") { std::array notified = {0, 0, 0}; std::array, 3> locks = { - group->lock(0, 1, [&] { ++notified[0]; }), - group->lock(1, 1, [&] { ++notified[1]; }), - group->lock(2, 1, [&] { ++notified[2]; }), + group->lock(0, util::Priority::LOW, [&] { ++notified[0]; }), + group->lock(1, util::Priority::LOW, [&] { ++notified[1]; }), + group->lock(2, util::Priority::LOW, [&] { ++notified[2]; }), }; THEN("Locking and then unlocking the second lock") { @@ -316,7 +316,7 @@ namespace threading { WHEN("Creating a lock and locking it") { int notified1 = 0; - std::unique_ptr lock1 = group->lock(1, 1, [&] { ++notified1; }); + std::unique_ptr lock1 = group->lock(1, util::Priority::LOW, [&] { ++notified1; }); lock1->lock(); THEN("The lock should be true") { @@ -325,7 +325,7 @@ namespace threading { AND_WHEN("Creating a second lock with a higher priority") { int notified2 = 0; - std::unique_ptr lock2 = group->lock(2, 2, [&] { ++notified2; }); + std::unique_ptr lock2 = group->lock(2, util::Priority::NORMAL, [&] { ++notified2; }); THEN("The new lock should be false") { CHECK(lock1->lock() == true); @@ -352,7 +352,7 @@ namespace threading { WHEN("Creating a lock and locking it") { int notified1 = 0; - std::unique_ptr lock1 = group->lock(1, 1, [&] { ++notified1; }); + std::unique_ptr lock1 = group->lock(1, util::Priority::LOW, [&] { ++notified1; }); lock1->lock(); THEN("The lock should be true") { @@ -361,7 +361,7 @@ namespace threading { AND_WHEN("Adding a second lock") { int notified2 = 0; - std::unique_ptr lock2 = group->lock(2, 1, [&] { ++notified2; }); + std::unique_ptr lock2 = group->lock(2, util::Priority::LOW, [&] { ++notified2; }); THEN("The second lock should be false") { CHECK(lock2->lock() == false); @@ -378,7 +378,7 @@ namespace threading { AND_WHEN("Adding a third lock with higher priority") { int notified3 = 0; - std::unique_ptr lock3 = group->lock(3, 2, [&] { ++notified3; }); + std::unique_ptr lock3 = group->lock(3, util::Priority::NORMAL, [&] { ++notified3; }); THEN("The third lock should be lockable and second lock should not") { CHECK(lock3->lock() == true); From 078dfd9853ff1bdfc8080d04249f1a88fd50ca7a Mon Sep 17 00:00:00 2001 From: Trent Houliston Date: Mon, 23 Dec 2024 21:33:12 +1100 Subject: [PATCH 04/73] Restore the API keeping the enums, and make ThreadPriority be an RAII object to better handle setting and restoring it. Also change what each of the levels mean now that enums let specific behaviour be assigned. Have also snuck in some extra changes in this commit that I'll be pulling out into other PRs --- src/LogLevel.hpp | 215 +++++++++++++------ src/PowerPlant.hpp | 15 +- src/PriorityLevel.hpp | 144 +++++++++++++ src/{LogLevel.cpp => Reactor.cpp} | 36 +--- src/Reactor.hpp | 21 +- src/dsl/Parse.hpp | 6 +- src/dsl/fusion/NoOp.hpp | 4 +- src/dsl/fusion/PriorityFusion.hpp | 4 +- src/dsl/word/Always.hpp | 17 +- src/dsl/word/Priority.hpp | 41 +++- src/dsl/word/Shutdown.hpp | 5 +- src/dsl/word/emit/Initialise.hpp | 2 +- src/extension/ChronoController.cpp | 2 +- src/threading/ReactionTask.hpp | 4 +- src/threading/scheduler/Group.cpp | 6 +- src/threading/scheduler/Group.hpp | 8 +- src/threading/scheduler/Pool.cpp | 6 +- src/threading/scheduler/Scheduler.cpp | 2 +- src/threading/scheduler/Scheduler.hpp | 2 +- src/util/CallbackGenerator.hpp | 6 +- src/util/ThreadPriority.cpp | 106 +++++++++ src/util/ThreadPriority.hpp | 59 +++++ src/util/{Priority.hpp => from_string.hpp} | 27 +-- src/util/update_current_thread_priority.hpp | 72 ------- tests/tests/api/ReactionHandle.cpp | 4 +- tests/tests/api/ReactionStatistics.cpp | 4 +- tests/tests/api/ReactionStatisticsTiming.cpp | 4 +- tests/tests/dsl/BlockNoData.cpp | 2 +- tests/tests/dsl/Buffer.cpp | 10 +- tests/tests/dsl/DSLOrdering.cpp | 6 +- tests/tests/dsl/FlagMessage.cpp | 2 +- tests/tests/dsl/IdleGlobal.cpp | 2 +- tests/tests/dsl/IdleSingle.cpp | 4 +- tests/tests/dsl/Once.cpp | 2 +- tests/tests/dsl/Priority.cpp | 44 ++-- tests/tests/dsl/RawFunction.cpp | 8 +- tests/tests/dsl/Shutdown.cpp | 2 +- tests/tests/dsl/Transient.cpp | 18 +- tests/tests/dsl/With.cpp | 10 +- tests/tests/log/Log.cpp | 122 ++++++----- tests/tests/threading/Group.cpp | 64 +++--- 41 files changed, 727 insertions(+), 391 deletions(-) create mode 100644 src/PriorityLevel.hpp rename src/{LogLevel.cpp => Reactor.cpp} (57%) create mode 100644 src/util/ThreadPriority.cpp create mode 100644 src/util/ThreadPriority.hpp rename src/util/{Priority.hpp => from_string.hpp} (67%) delete mode 100644 src/util/update_current_thread_priority.hpp diff --git a/src/LogLevel.hpp b/src/LogLevel.hpp index 148a4e140..0e9322aeb 100644 --- a/src/LogLevel.hpp +++ b/src/LogLevel.hpp @@ -33,99 +33,182 @@ namespace NUClear { -/** - * LogLevel defines the different levels log messages can be set to. - * - * Log levels are used to provide different levels of detail on a per-reactor basis. - * The logging level of a reactor can be changed by setting it in the install function. - */ -enum LogLevel : uint8_t { +class LogLevel { +public: /** - * Don't use this log level when emitting logs, it is for setting reactor log level from non reactor sources. + * LogLevel defines the different levels log messages can be set to. * - * Specifically when a NUClear::log is called from code that is not running in a reaction (even transitively) then - * the reactor_level will be set to UNKNOWN. + * Log levels are used to provide different levels of detail on a per-reactor basis. + * The logging level of a reactor can be changed by setting it in the install function. */ - UNKNOWN, + enum Value : uint8_t { + /** + * Don't use this log level when emitting logs, it is for setting reactor log level from non reactor sources. + * + * Specifically when a NUClear::log is called from code that is not running in a reaction (even transitively) + * then the reactor_level will be set to UNKNOWN. + */ + UNKNOWN, + + /** + * The Trace level contains messages that are used to trace the exact flow of execution. + * + * This level is extremely verbose and often has a message per line of code. + */ + TRACE, + + /** + * Debug contains messages that represent the inputs and outputs of different computation units. + * + * If you have a function that performs three steps to do something then it's likely that you will have a + * message for the input and output of those three steps. Additionally you would likely have messages that check + * if it hit different branches. + */ + DEBUG, + + /** + * The info level is used to provide high level goal messages such as function start or successful completion. + * + * This shows when key user-facing functionality is executed and tells us that everything is working without + * getting into the details. + */ + INFO, + + /** + * The warning level is used to notify us that everything might not be working perfectly. + * + * Warnings are errors or inconsistencies that aren't fatal and generally do not completely break the system. + * However a warning message should require action and should point to a section of the system that needs + * attention. + */ + WARN, + + /** + * The error level is used to report unexpected behavior. + + * This level doesn't need to prefix a program-crashing issue but should be used to report major unexpected + branches + * in logic or other constraint breaking problems such as failed assertions. + * All errors should require action from someone and should be addressed immediately. + */ + ERROR, + + /** + * Fatal is a program destroying error that needs to be addressed immediately. + * + * If a fatal message is sent it should point to something that should never ever happen and ideally provide as + * much information as possible as to why it crashed. Fatal messages require action immediately and should + * always be addressed. + */ + FATAL + }; /** - * The Trace level contains messages that are used to trace the exact flow of execution. + * Construct a LogLevel from a Value * - * This level is extremely verbose and often has a message per line of code. + * @param value The value to construct the LogLevel from */ - TRACE, + constexpr LogLevel(const Value& value = Value::UNKNOWN) : value(value) {}; /** - * Debug contains messages that represent the inputs and outputs of different computation units. + * Construct a LogLevel from a string * - * If you have a function that performs three steps to do something then it's likely that you will have a message - * for the input and output of those three steps. - * Additionally you would likely have messages that check if it hit different branches. + * @param level The string to construct the LogLevel from */ - DEBUG, + constexpr LogLevel(const std::string& level) + : value(level == "TRACE" ? LogLevel::TRACE + : level == "DEBUG" ? LogLevel::DEBUG + : level == "INFO" ? LogLevel::INFO + : level == "WARN" ? LogLevel::WARN + : level == "ERROR" ? LogLevel::ERROR + : level == "FATAL" ? LogLevel::FATAL + : LogLevel::UNKNOWN) {}; /** - * The info level is used to provide high level goal messages such as function start or successful completion. + * A call operator which will return the value of the LogLevel + * This can be useful in situations where the implicit conversion operators are ambiguous. * - * This shows when key user-facing functionality is executed and tells us that everything is working without getting - * into the details. + * @return The value of the LogLevel */ - INFO, + constexpr Value operator()() const { + return value; + } /** - * The warning level is used to notify us that everything might not be working perfectly. + * A conversion operator which will return the value of the LogLevel * - * Warnings are errors or inconsistencies that aren't fatal and generally do not completely break the system. - * However a warning message should require action and should point to a section of the system that needs attention. + * @return The value of the LogLevel */ - WARN, + constexpr operator Value() const { + return value; + } /** - * The error level is used to report unexpected behavior. - - * This level doesn't need to prefix a program-crashing issue but should be used to report major unexpected branches - * in logic or other constraint breaking problems such as failed assertions. - * All errors should require action from someone and should be addressed immediately. + * A conversion operator which will return the string representation of the LogLevel + * + * @return The string representation of the LogLevel */ - ERROR, + operator std::string() const { + return value == LogLevel::TRACE ? "TRACE" + : value == LogLevel::DEBUG ? "DEBUG" + : value == LogLevel::INFO ? "INFO" + : value == LogLevel::WARN ? "WARN" + : value == LogLevel::ERROR ? "ERROR" + : value == LogLevel::FATAL ? "FATAL" + : "UNKNOWN"; + } /** - * Fatal is a program destroying error that needs to be addressed immediately. + * Stream the LogLevel to an ostream, it will output the string representation of the LogLevel + * + * @param os The ostream to output to + * @param level The LogLevel to output * - * If a fatal message is sent it should point to something that should never ever happen and ideally provide as much - * information as possible as to why it crashed. - * Fatal messages require action immediately and should always be addressed. + * @return The ostream that was passed in */ - FATAL + friend std::ostream& operator<<(std::ostream& os, LogLevel level) { + return os << static_cast(level); + } + + friend constexpr bool operator<(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value < rhs.value; + } + friend constexpr bool operator>(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value > rhs.value; + } + friend constexpr bool operator<=(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value <= rhs.value; + } + friend constexpr bool operator>=(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value >= rhs.value; + } + friend constexpr bool operator==(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value == rhs.value; + } + friend constexpr bool operator!=(const LogLevel& lhs, const LogLevel& rhs) { + return lhs.value != rhs.value; + } + friend constexpr bool operator<(const LogLevel& lhs, const Value& rhs) { + return lhs.value < rhs; + } + friend constexpr bool operator>(const LogLevel& lhs, const Value& rhs) { + return lhs.value > rhs; + } + friend constexpr bool operator<=(const LogLevel& lhs, const Value& rhs) { + return lhs.value <= rhs; + } + friend constexpr bool operator>=(const LogLevel& lhs, const Value& rhs) { + return lhs.value >= rhs; + } + friend constexpr bool operator==(const LogLevel& lhs, const Value& rhs) { + return lhs.value == rhs; + } + + +private: + /// The stored enum value + Value value; }; - -/** - * This function is used to convert a LogLevel into a string - * - * @param level the LogLevel to convert - * - * @return the string representation of the LogLevel - */ -std::string to_string(const LogLevel& level); - -/** - * This function is used to convert a string into a LogLevel - * - * @param level the string to convert - * - * @return the LogLevel representation of the string - */ -LogLevel from_string(const std::string& level); - -/** - * This function is used to convert a LogLevel into a string for printing. - * - * @param os the output stream to write to - * @param level the LogLevel to convert - * @return the output stream - */ -std::ostream& operator<<(std::ostream& os, const LogLevel& level); - } // namespace NUClear #endif // NUCLEAR_LOGLEVEL_HPP diff --git a/src/PowerPlant.hpp b/src/PowerPlant.hpp index c7dc6969c..f9bc95824 100644 --- a/src/PowerPlant.hpp +++ b/src/PowerPlant.hpp @@ -193,7 +193,7 @@ class PowerPlant { * * @param args The arguments we are logging */ - template + template void log(Arguments&&... args) { log(level, std::forward(args)...); } @@ -235,8 +235,7 @@ class PowerPlant { emit(std::move(data)); } template