From fa7859606bb09416c0b6a9ff3e7b82389170893b Mon Sep 17 00:00:00 2001 From: Adrian Wann <95002807+AWann2@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:15:02 +1100 Subject: [PATCH] Update nuclear subtree (#24) Updating the nuclear subtree to fix the memory leak in NUClearNet. The fix: (https://github.com/Fastcode/NUClear/commit/2b46a4d5532fb60f2025651a16c0b9104f43675c) --- package-lock.json | 4 +- package.json | 2 +- src/nuclear/.clang-tidy | 12 +- src/nuclear/.cmake-format.py | 5 +- src/nuclear/.github/workflows/gcc.yaml | 24 +- src/nuclear/.github/workflows/linting.yaml | 38 +- src/nuclear/.github/workflows/macos.yaml | 20 +- src/nuclear/.github/workflows/sonarcloud.yaml | 30 +- src/nuclear/.github/workflows/windows.yaml | 21 +- src/nuclear/.vscode/settings.json | 84 +--- src/nuclear/CMakeLists.txt | 59 +-- src/nuclear/cmake/ClangTidy.cmake | 22 + src/nuclear/cmake/CompilerOptions.cmake | 7 + src/nuclear/cmake/Modules/FindCATCH.cmake | 29 -- src/nuclear/cmake/Modules/HeaderLibrary.cmake | 93 ---- src/nuclear/cmake/Sanitizers.cmake | 47 ++ src/nuclear/docs/CMakeLists.txt | 35 +- src/nuclear/docs/dsl.rst | 41 +- src/nuclear/docs/extension.rst | 25 +- src/nuclear/docs/networking.rst | 6 +- src/nuclear/docs/requirements.txt | 5 +- src/nuclear/docs/startup.rst | 12 +- src/nuclear/src/CMakeLists.txt | 2 +- src/nuclear/src/Configuration.hpp | 7 +- src/nuclear/src/Environment.hpp | 13 +- src/nuclear/src/LogLevel.cpp | 56 +++ src/nuclear/src/LogLevel.hpp | 95 ++-- src/nuclear/src/PowerPlant.cpp | 106 +++-- src/nuclear/src/PowerPlant.hpp | 332 ++++++++------ src/nuclear/src/PowerPlant.ipp | 189 -------- src/nuclear/src/Reactor.hpp | 132 ++++-- src/nuclear/src/clock.cpp | 85 ++++ src/nuclear/src/clock.hpp | 83 +++- src/nuclear/src/dsl/Fusion.hpp | 24 +- src/nuclear/src/dsl/Parse.hpp | 54 ++- src/nuclear/src/dsl/fusion/BindFusion.hpp | 70 +-- .../dsl/fusion/{has_get.hpp => FindWords.hpp} | 37 +- src/nuclear/src/dsl/fusion/GetFusion.hpp | 66 +-- src/nuclear/src/dsl/fusion/GroupFusion.hpp | 60 +-- src/nuclear/src/dsl/fusion/InlineFusion.hpp | 82 ++++ src/nuclear/src/dsl/fusion/NoOp.hpp | 72 ++-- src/nuclear/src/dsl/fusion/PoolFusion.hpp | 54 +-- src/nuclear/src/dsl/fusion/PostRunFusion.hpp | 76 ++++ .../src/dsl/fusion/PostconditionFusion.hpp | 109 ----- src/nuclear/src/dsl/fusion/PreRunFusion.hpp | 76 ++++ .../src/dsl/fusion/PreconditionFusion.hpp | 61 +-- src/nuclear/src/dsl/fusion/PriorityFusion.hpp | 58 +-- src/nuclear/src/dsl/fusion/ScopeFusion.hpp | 84 ++++ .../src/dsl/fusion/has_nuclear_dsl_method.hpp | 60 +++ src/nuclear/src/dsl/fusion/has_pool.hpp | 58 --- .../src/dsl/fusion/has_postcondition.hpp | 59 --- .../src/dsl/fusion/has_precondition.hpp | 59 --- src/nuclear/src/dsl/fusion/has_priority.hpp | 58 --- src/nuclear/src/dsl/operation/CacheGet.hpp | 17 +- src/nuclear/src/dsl/operation/ChronoTask.hpp | 63 ++- src/nuclear/src/dsl/operation/DSLProxy.hpp | 17 +- src/nuclear/src/dsl/operation/TypeBind.hpp | 64 ++- src/nuclear/src/dsl/operation/Unbind.hpp | 12 +- src/nuclear/src/dsl/store/DataStore.hpp | 9 +- src/nuclear/src/dsl/store/ThreadStore.hpp | 16 +- .../src/dsl/store/TypeCallbackStore.hpp | 12 +- src/nuclear/src/dsl/trait/is_transient.hpp | 20 +- src/nuclear/src/dsl/validation/Validation.hpp | 10 +- src/nuclear/src/dsl/word/Always.hpp | 138 +++--- src/nuclear/src/dsl/word/Buffer.hpp | 18 +- src/nuclear/src/dsl/word/Every.hpp | 52 +-- src/nuclear/src/dsl/word/Group.hpp | 84 ++-- src/nuclear/src/dsl/word/IO.hpp | 78 ++-- src/nuclear/src/dsl/word/Idle.hpp | 91 ++++ src/nuclear/src/dsl/word/Inline.hpp | 72 ++++ src/nuclear/src/dsl/word/Last.hpp | 78 ++-- src/nuclear/src/dsl/word/MainThread.hpp | 36 +- src/nuclear/src/dsl/word/Network.hpp | 37 +- src/nuclear/src/dsl/word/Once.hpp | 18 +- src/nuclear/src/dsl/word/Optional.hpp | 41 +- src/nuclear/src/dsl/word/Pool.hpp | 99 +++-- src/nuclear/src/dsl/word/Priority.hpp | 44 +- src/nuclear/src/dsl/word/Shutdown.hpp | 23 +- src/nuclear/src/dsl/word/Single.hpp | 12 +- src/nuclear/src/dsl/word/Startup.hpp | 16 +- src/nuclear/src/dsl/word/Sync.hpp | 40 +- src/nuclear/src/dsl/word/TCP.hpp | 54 ++- src/nuclear/src/dsl/word/TaskScope.hpp | 108 +++++ src/nuclear/src/dsl/word/Trigger.hpp | 24 +- src/nuclear/src/dsl/word/UDP.hpp | 137 +++--- src/nuclear/src/dsl/word/Watchdog.hpp | 112 +++-- src/nuclear/src/dsl/word/With.hpp | 36 +- src/nuclear/src/dsl/word/emit/Delay.hpp | 32 +- src/nuclear/src/dsl/word/emit/Initialise.hpp | 51 ++- .../dsl/word/emit/{Direct.hpp => Inline.hpp} | 39 +- src/nuclear/src/dsl/word/emit/Local.hpp | 15 +- src/nuclear/src/dsl/word/emit/Network.hpp | 38 +- src/nuclear/src/dsl/word/emit/UDP.hpp | 27 +- src/nuclear/src/dsl/word/emit/Watchdog.hpp | 81 ++-- .../src/extension/ChronoController.cpp | 201 +++++++++ .../src/extension/ChronoController.hpp | 148 +------ .../extension/IOController.cpp} | 9 +- src/nuclear/src/extension/IOController.hpp | 125 +++++- .../src/extension/IOController_Posix.hpp | 393 ----------------- .../src/extension/IOController_Posix.ipp | 289 +++++++++++++ .../src/extension/IOController_Windows.hpp | 336 --------------- .../src/extension/IOController_Windows.ipp | 263 ++++++++++++ .../src/extension/NetworkController.cpp | 155 +++++++ .../src/extension/NetworkController.hpp | 140 +----- src/nuclear/src/extension/TraceController.cpp | 306 +++++++++++++ src/nuclear/src/extension/TraceController.hpp | 113 +++++ .../src/extension/network/NUClearNetwork.cpp | 38 +- .../src/extension/network/NUClearNetwork.hpp | 144 ++++--- .../src/extension/network/wire_protocol.hpp | 14 +- .../src/extension/trace/StringInterner.hpp | 111 +++++ src/nuclear/src/extension/trace/protobuf.cpp | 143 ++++++ src/nuclear/src/extension/trace/protobuf.hpp | 133 ++++++ src/nuclear/src/id.hpp | 8 +- .../src/message/CommandLineArguments.hpp | 4 +- src/nuclear/src/message/LogMessage.hpp | 37 +- .../src/message/NetworkConfiguration.hpp | 22 +- src/nuclear/src/message/NetworkEvent.hpp | 14 +- .../ReactionStatistics.cpp} | 64 ++- .../src/message/ReactionStatistics.hpp | 126 ++++-- src/nuclear/src/message/TimeTravel.hpp | 63 +++ src/nuclear/src/message/Trace.hpp | 49 +++ src/nuclear/src/nuclear.in | 16 + src/nuclear/src/threading/Reaction.cpp | 33 +- src/nuclear/src/threading/Reaction.hpp | 116 +++-- .../ReactionHandle.cpp} | 85 ++-- src/nuclear/src/threading/ReactionHandle.hpp | 107 ++--- .../src/threading/ReactionIdentifiers.hpp | 26 +- src/nuclear/src/threading/ReactionTask.cpp | 100 +++++ src/nuclear/src/threading/ReactionTask.hpp | 213 ++++----- src/nuclear/src/threading/TaskScheduler.cpp | 274 ------------ src/nuclear/src/threading/TaskScheduler.hpp | 262 ----------- .../src/threading/scheduler/CombinedLock.cpp | 52 +++ .../src/threading/scheduler/CombinedLock.hpp | 72 ++++ .../src/threading/scheduler/CountingLock.cpp | 45 ++ .../src/threading/scheduler/CountingLock.hpp | 81 ++++ src/nuclear/src/threading/scheduler/Group.cpp | 137 ++++++ src/nuclear/src/threading/scheduler/Group.hpp | 178 ++++++++ .../scheduler/Lock.cpp} | 15 +- src/nuclear/src/threading/scheduler/Lock.hpp | 60 +++ src/nuclear/src/threading/scheduler/Pool.cpp | 281 ++++++++++++ src/nuclear/src/threading/scheduler/Pool.hpp | 258 +++++++++++ .../src/threading/scheduler/Scheduler.cpp | 200 +++++++++ .../src/threading/scheduler/Scheduler.hpp | 167 +++++++ src/nuclear/src/util/CallableInfo.hpp | 127 +++--- src/nuclear/src/util/CallbackGenerator.hpp | 127 +++--- src/nuclear/src/util/FileDescriptor.cpp | 1 + src/nuclear/src/util/FileDescriptor.hpp | 33 +- src/nuclear/src/util/FunctionFusion.hpp | 190 ++++---- src/nuclear/src/util/GeneratedCallback.hpp | 65 --- src/nuclear/src/util/GroupDescriptor.hpp | 29 +- .../util/{main_thread_id.hpp => Inline.hpp} | 31 +- src/nuclear/src/util/MergeTransient.hpp | 8 +- src/nuclear/src/util/MetaProgramming.hpp | 6 +- src/nuclear/src/util/RelevantArguments.hpp | 19 +- src/nuclear/src/util/Sequence.hpp | 16 +- src/nuclear/src/util/ThreadPoolDescriptor.hpp | 46 +- .../src/util/TransientDataElements.hpp | 11 +- src/nuclear/src/util/TypeList.hpp | 6 +- src/nuclear/src/util/TypeMap.hpp | 72 ++-- src/nuclear/src/util/apply.hpp | 15 +- src/nuclear/src/util/demangle.cpp | 10 +- src/nuclear/src/util/demangle.hpp | 2 +- src/nuclear/src/util/get_hostname.cpp | 48 +++ src/nuclear/src/util/get_hostname.hpp | 26 +- src/nuclear/src/util/meta/Filter.hpp | 79 ++++ .../src/util/network/get_interfaces.cpp | 4 +- .../src/util/network/get_interfaces.hpp | 26 +- .../util/network/if_number_from_address.cpp | 6 + .../util/network/if_number_from_address.hpp | 11 +- src/nuclear/src/util/network/resolve.cpp | 5 +- src/nuclear/src/util/network/resolve.hpp | 14 +- src/nuclear/src/util/network/sock_t.hpp | 4 +- src/nuclear/src/util/platform.cpp | 4 +- src/nuclear/src/util/platform.hpp | 8 +- src/nuclear/src/util/precise_sleep.cpp | 12 +- src/nuclear/src/util/serialise/Serialise.hpp | 18 +- src/nuclear/src/util/serialise/xxhash.cpp | 9 +- src/nuclear/src/util/serialise/xxhash.hpp | 8 +- src/nuclear/src/util/tuplify.hpp | 2 + src/nuclear/src/util/unpack.hpp | 30 +- src/nuclear/src/util/usage_clock.cpp | 44 ++ src/nuclear/src/util/usage_clock.hpp | 30 ++ src/nuclear/tests/CMakeLists.txt | 116 +++-- src/nuclear/tests/individual/BaseClock.cpp | 140 ------ src/nuclear/tests/individual/CustomClock.cpp | 93 ---- src/nuclear/tests/network/NUClearNetwork.cpp | 121 ------ src/nuclear/tests/networktest.cpp | 68 +-- src/nuclear/tests/test_util/TestBase.hpp | 66 ++- src/nuclear/tests/test_util/TimeUnit.hpp | 65 +++ src/nuclear/tests/test_util/common.hpp | 51 +++ src/nuclear/tests/test_util/diff_string.cpp | 2 + .../tests/test_util/executable_path.cpp | 78 ++++ .../test_util/executable_path.hpp} | 23 +- src/nuclear/tests/test_util/has_ipv6.cpp | 2 +- src/nuclear/tests/test_util/has_ipv6.hpp | 2 +- .../tests/{ => tests}/api/ReactionHandle.cpp | 40 +- .../{ => tests}/api/ReactionStatistics.cpp | 61 +-- .../tests/api/ReactionStatisticsTiming.cpp | 182 ++++++++ .../tests/{ => tests}/api/ReactorArgs.cpp | 12 +- src/nuclear/tests/tests/api/TimeTravel.cpp | 141 ++++++ .../tests/tests/api/TimeTravelFrozen.cpp | 125 ++++++ src/nuclear/tests/{ => tests}/dsl/Always.cpp | 24 +- .../tests/{ => tests}/dsl/ArgumentFission.cpp | 30 +- .../tests/{ => tests}/dsl/BlockNoData.cpp | 31 +- src/nuclear/tests/{ => tests}/dsl/Buffer.cpp | 42 +- .../{ => tests}/dsl/CommandLineArguments.cpp | 24 +- .../tests/{ => tests}/dsl/CustomGet.cpp | 28 +- .../tests/{ => tests}/dsl/DSLOrdering.cpp | 39 +- .../tests/{ => tests}/dsl/DSLProxy.cpp | 31 +- src/nuclear/tests/{ => tests}/dsl/Every.cpp | 43 +- .../tests/{ => tests}/dsl/FlagMessage.cpp | 36 +- .../tests/{ => tests}/dsl/FusionInOrder.cpp | 12 +- src/nuclear/tests/tests/dsl/GroupPool.cpp | 117 +++++ src/nuclear/tests/{ => tests}/dsl/IO.cpp | 44 +- src/nuclear/tests/tests/dsl/Idle.cpp | 141 ++++++ src/nuclear/tests/tests/dsl/IdleCrossPool.cpp | 90 ++++ src/nuclear/tests/tests/dsl/IdleGlobal.cpp | 126 ++++++ src/nuclear/tests/tests/dsl/IdleSingle.cpp | 147 +++++++ .../tests/tests/dsl/IdleSingleGlobal.cpp | 121 ++++++ src/nuclear/tests/tests/dsl/IdleSync.cpp | 91 ++++ src/nuclear/tests/tests/dsl/Inline.cpp | 140 ++++++ src/nuclear/tests/{ => tests}/dsl/Last.cpp | 31 +- .../tests/{ => tests}/dsl/MainThread.cpp | 46 +- .../{ => tests}/dsl/MissingArguments.cpp | 36 +- src/nuclear/tests/{ => tests}/dsl/Once.cpp | 34 +- .../tests/{ => tests}/dsl/Optional.cpp | 34 +- .../tests/{ => tests}/dsl/Priority.cpp | 66 +-- src/nuclear/tests/{ => tests}/dsl/Raw.cpp | 45 +- .../tests/{ => tests}/dsl/RawFunction.cpp | 32 +- .../tests/{ => tests}/dsl/Shutdown.cpp | 25 +- src/nuclear/tests/{ => tests}/dsl/Startup.cpp | 34 +- src/nuclear/tests/{ => tests}/dsl/Sync.cpp | 50 +-- src/nuclear/tests/tests/dsl/SyncMulti.cpp | 88 ++++ .../tests/{ => tests}/dsl/SyncOrder.cpp | 59 +-- src/nuclear/tests/{ => tests}/dsl/TCP.cpp | 23 +- src/nuclear/tests/tests/dsl/TaskScope.cpp | 145 +++++++ .../tests/{ => tests}/dsl/Transient.cpp | 38 +- src/nuclear/tests/{ => tests}/dsl/Trigger.cpp | 33 +- src/nuclear/tests/{ => tests}/dsl/UDP.cpp | 22 +- .../tests/{ => tests}/dsl/Watchdog.cpp | 60 ++- src/nuclear/tests/{ => tests}/dsl/With.cpp | 42 +- .../tests/{ => tests}/dsl/emit/Delay.cpp | 104 +++-- .../tests/{ => tests}/dsl/emit/EmitFusion.cpp | 36 +- .../tests/{ => tests}/dsl/emit/Initialise.cpp | 37 +- src/nuclear/tests/{ => tests}/log/Log.cpp | 33 +- .../tests/tests/threading/CountingLock.cpp | 76 ++++ src/nuclear/tests/tests/threading/Group.cpp | 406 ++++++++++++++++++ .../tests/{ => tests}/util/demangle.cpp | 7 +- .../{ => tests}/util/network/resolve.cpp | 4 +- .../{ => tests}/util/serialise/serialise.cpp | 19 +- .../{ => tests}/util/serialise/xxhash.cpp | 5 +- 251 files changed, 10659 insertions(+), 6153 deletions(-) create mode 100644 src/nuclear/cmake/ClangTidy.cmake create mode 100644 src/nuclear/cmake/CompilerOptions.cmake delete mode 100644 src/nuclear/cmake/Modules/FindCATCH.cmake delete mode 100644 src/nuclear/cmake/Modules/HeaderLibrary.cmake create mode 100644 src/nuclear/cmake/Sanitizers.cmake create mode 100644 src/nuclear/src/LogLevel.cpp delete mode 100644 src/nuclear/src/PowerPlant.ipp create mode 100644 src/nuclear/src/clock.cpp rename src/nuclear/src/dsl/fusion/{has_get.hpp => FindWords.hpp} (61%) create mode 100644 src/nuclear/src/dsl/fusion/InlineFusion.hpp create mode 100644 src/nuclear/src/dsl/fusion/PostRunFusion.hpp delete mode 100644 src/nuclear/src/dsl/fusion/PostconditionFusion.hpp create mode 100644 src/nuclear/src/dsl/fusion/PreRunFusion.hpp create mode 100644 src/nuclear/src/dsl/fusion/ScopeFusion.hpp create mode 100644 src/nuclear/src/dsl/fusion/has_nuclear_dsl_method.hpp delete mode 100644 src/nuclear/src/dsl/fusion/has_pool.hpp delete mode 100644 src/nuclear/src/dsl/fusion/has_postcondition.hpp delete mode 100644 src/nuclear/src/dsl/fusion/has_precondition.hpp delete mode 100644 src/nuclear/src/dsl/fusion/has_priority.hpp create mode 100644 src/nuclear/src/dsl/word/Idle.hpp create mode 100644 src/nuclear/src/dsl/word/Inline.hpp create mode 100644 src/nuclear/src/dsl/word/TaskScope.hpp rename src/nuclear/src/dsl/word/emit/{Direct.hpp => Inline.hpp} (67%) create mode 100644 src/nuclear/src/extension/ChronoController.cpp rename src/nuclear/{tests/test.cpp => src/extension/IOController.cpp} (88%) delete mode 100644 src/nuclear/src/extension/IOController_Posix.hpp create mode 100644 src/nuclear/src/extension/IOController_Posix.ipp delete mode 100644 src/nuclear/src/extension/IOController_Windows.hpp create mode 100644 src/nuclear/src/extension/IOController_Windows.ipp create mode 100644 src/nuclear/src/extension/NetworkController.cpp create mode 100644 src/nuclear/src/extension/TraceController.cpp create mode 100644 src/nuclear/src/extension/TraceController.hpp create mode 100644 src/nuclear/src/extension/trace/StringInterner.hpp create mode 100644 src/nuclear/src/extension/trace/protobuf.cpp create mode 100644 src/nuclear/src/extension/trace/protobuf.hpp rename src/nuclear/src/{dsl/fusion/has_bind.hpp => message/ReactionStatistics.cpp} (50%) create mode 100644 src/nuclear/src/message/TimeTravel.hpp create mode 100644 src/nuclear/src/message/Trace.hpp rename src/nuclear/src/{dsl/fusion/has_group.hpp => threading/ReactionHandle.cpp} (50%) create mode 100644 src/nuclear/src/threading/ReactionTask.cpp delete mode 100644 src/nuclear/src/threading/TaskScheduler.cpp delete mode 100644 src/nuclear/src/threading/TaskScheduler.hpp create mode 100644 src/nuclear/src/threading/scheduler/CombinedLock.cpp create mode 100644 src/nuclear/src/threading/scheduler/CombinedLock.hpp create mode 100644 src/nuclear/src/threading/scheduler/CountingLock.cpp create mode 100644 src/nuclear/src/threading/scheduler/CountingLock.hpp create mode 100644 src/nuclear/src/threading/scheduler/Group.cpp create mode 100644 src/nuclear/src/threading/scheduler/Group.hpp rename src/nuclear/src/{util/main_thread_id.cpp => threading/scheduler/Lock.cpp} (84%) create mode 100644 src/nuclear/src/threading/scheduler/Lock.hpp create mode 100644 src/nuclear/src/threading/scheduler/Pool.cpp create mode 100644 src/nuclear/src/threading/scheduler/Pool.hpp create mode 100644 src/nuclear/src/threading/scheduler/Scheduler.cpp create mode 100644 src/nuclear/src/threading/scheduler/Scheduler.hpp delete mode 100644 src/nuclear/src/util/GeneratedCallback.hpp rename src/nuclear/src/util/{main_thread_id.hpp => Inline.hpp} (69%) create mode 100644 src/nuclear/src/util/get_hostname.cpp create mode 100644 src/nuclear/src/util/meta/Filter.hpp create mode 100644 src/nuclear/src/util/usage_clock.cpp create mode 100644 src/nuclear/src/util/usage_clock.hpp delete mode 100644 src/nuclear/tests/individual/BaseClock.cpp delete mode 100644 src/nuclear/tests/individual/CustomClock.cpp delete mode 100644 src/nuclear/tests/network/NUClearNetwork.cpp create mode 100644 src/nuclear/tests/test_util/TimeUnit.hpp create mode 100644 src/nuclear/tests/test_util/common.hpp create mode 100644 src/nuclear/tests/test_util/executable_path.cpp rename src/nuclear/{src/util/ThreadPoolDescriptor.cpp => tests/test_util/executable_path.hpp} (76%) rename src/nuclear/tests/{ => tests}/api/ReactionHandle.cpp (80%) rename src/nuclear/tests/{ => tests}/api/ReactionStatistics.cpp (69%) create mode 100644 src/nuclear/tests/tests/api/ReactionStatisticsTiming.cpp rename src/nuclear/tests/{ => tests}/api/ReactorArgs.cpp (92%) create mode 100644 src/nuclear/tests/tests/api/TimeTravel.cpp create mode 100644 src/nuclear/tests/tests/api/TimeTravelFrozen.cpp rename src/nuclear/tests/{ => tests}/dsl/Always.cpp (87%) rename src/nuclear/tests/{ => tests}/dsl/ArgumentFission.cpp (84%) rename src/nuclear/tests/{ => tests}/dsl/BlockNoData.cpp (82%) rename src/nuclear/tests/{ => tests}/dsl/Buffer.cpp (87%) rename src/nuclear/tests/{ => tests}/dsl/CommandLineArguments.cpp (83%) rename src/nuclear/tests/{ => tests}/dsl/CustomGet.cpp (79%) rename src/nuclear/tests/{ => tests}/dsl/DSLOrdering.cpp (79%) rename src/nuclear/tests/{ => tests}/dsl/DSLProxy.cpp (81%) rename src/nuclear/tests/{ => tests}/dsl/Every.cpp (70%) rename src/nuclear/tests/{ => tests}/dsl/FlagMessage.cpp (82%) rename src/nuclear/tests/{ => tests}/dsl/FusionInOrder.cpp (90%) create mode 100644 src/nuclear/tests/tests/dsl/GroupPool.cpp rename src/nuclear/tests/{ => tests}/dsl/IO.cpp (83%) create mode 100644 src/nuclear/tests/tests/dsl/Idle.cpp create mode 100644 src/nuclear/tests/tests/dsl/IdleCrossPool.cpp create mode 100644 src/nuclear/tests/tests/dsl/IdleGlobal.cpp create mode 100644 src/nuclear/tests/tests/dsl/IdleSingle.cpp create mode 100644 src/nuclear/tests/tests/dsl/IdleSingleGlobal.cpp create mode 100644 src/nuclear/tests/tests/dsl/IdleSync.cpp create mode 100644 src/nuclear/tests/tests/dsl/Inline.cpp rename src/nuclear/tests/{ => tests}/dsl/Last.cpp (84%) rename src/nuclear/tests/{ => tests}/dsl/MainThread.cpp (73%) rename src/nuclear/tests/{ => tests}/dsl/MissingArguments.cpp (83%) rename src/nuclear/tests/{ => tests}/dsl/Once.cpp (86%) rename src/nuclear/tests/{ => tests}/dsl/Optional.cpp (81%) rename src/nuclear/tests/{ => tests}/dsl/Priority.cpp (63%) rename src/nuclear/tests/{ => tests}/dsl/Raw.cpp (79%) rename src/nuclear/tests/{ => tests}/dsl/RawFunction.cpp (88%) rename src/nuclear/tests/{ => tests}/dsl/Shutdown.cpp (84%) rename src/nuclear/tests/{ => tests}/dsl/Startup.cpp (80%) rename src/nuclear/tests/{ => tests}/dsl/Sync.cpp (78%) create mode 100644 src/nuclear/tests/tests/dsl/SyncMulti.cpp rename src/nuclear/tests/{ => tests}/dsl/SyncOrder.cpp (61%) rename src/nuclear/tests/{ => tests}/dsl/TCP.cpp (95%) create mode 100644 src/nuclear/tests/tests/dsl/TaskScope.cpp rename src/nuclear/tests/{ => tests}/dsl/Transient.cpp (86%) rename src/nuclear/tests/{ => tests}/dsl/Trigger.cpp (80%) rename src/nuclear/tests/{ => tests}/dsl/UDP.cpp (98%) rename src/nuclear/tests/{ => tests}/dsl/Watchdog.cpp (67%) rename src/nuclear/tests/{ => tests}/dsl/With.cpp (82%) rename src/nuclear/tests/{ => tests}/dsl/emit/Delay.cpp (53%) rename src/nuclear/tests/{ => tests}/dsl/emit/EmitFusion.cpp (79%) rename src/nuclear/tests/{ => tests}/dsl/emit/Initialise.cpp (81%) rename src/nuclear/tests/{ => tests}/log/Log.cpp (93%) create mode 100644 src/nuclear/tests/tests/threading/CountingLock.cpp create mode 100644 src/nuclear/tests/tests/threading/Group.cpp rename src/nuclear/tests/{ => tests}/util/demangle.cpp (97%) rename src/nuclear/tests/{ => tests}/util/network/resolve.cpp (99%) rename src/nuclear/tests/{ => tests}/util/serialise/serialise.cpp (97%) rename src/nuclear/tests/{ => tests}/util/serialise/xxhash.cpp (98%) diff --git a/package-lock.json b/package-lock.json index 7c42a73c..376db8e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nuclearnet.js", - "version": "1.7.1", + "version": "1.7.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nuclearnet.js", - "version": "1.7.1", + "version": "1.7.2", "license": "MIT", "dependencies": { "bindings": "^1.5.0", diff --git a/package.json b/package.json index 9462a384..8f834039 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nuclearnet.js", - "version": "1.7.1", + "version": "1.7.2", "description": "Node.js module for interacting with the NUClear network", "main": "index.js", "types": "index.d.ts", diff --git a/src/nuclear/.clang-tidy b/src/nuclear/.clang-tidy index 6be676c2..6b4c31f3 100644 --- a/src/nuclear/.clang-tidy +++ b/src/nuclear/.clang-tidy @@ -7,6 +7,7 @@ Checks: > clang-diagnostic-*, clang-analyzer-*, cppcoreguidelines-*, + -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-owning-memory, @@ -26,7 +27,9 @@ Checks: > -misc-non-private-member-variables-in-classes, -misc-no-recursion, performance-*, + -performance-avoid-endl, readability-*, + -readability-avoid-nested-conditional-operator, -readability-function-cognitive-complexity, -readability-function-size, -readability-identifier-length, @@ -37,7 +40,6 @@ Checks: > -modernize-use-emplace WarningsAsErrors: "" HeaderFilterRegex: ".*" -AnalyzeTemporaryDtors: false FormatStyle: file CheckOptions: - key: cppcoreguidelines-avoid-magic-numbers.IgnoredFloatingPointValues @@ -49,9 +51,9 @@ CheckOptions: - key: readability-magic-numbers.IgnoredIntegerValues value: "1;2;3;4;" - key: llvm-namespace-comment.ShortNamespaceLines - value: '1' + value: "1" - key: llvm-namespace-comment.SpacesBeforeComments - value: '2' + value: "2" - key: misc-move-constructor-init.IncludeStyle value: google - key: modernize-loop-convert.NamingStyle @@ -61,10 +63,10 @@ CheckOptions: - key: modernize-replace-auto-ptr.IncludeStyle value: google - key: performance-for-range-copy.WarnOnAllAutoCopies - value: '1' + value: "1" - key: performance-type-promotion-in-math-fn.IncludeStyle value: google - key: performance-unnecessary-value-param.IncludeStyle value: google - key: readability-braces-around-statements.ShortStatementLines - value: '1' + value: "1" diff --git a/src/nuclear/.cmake-format.py b/src/nuclear/.cmake-format.py index 521da4e0..51321b88 100644 --- a/src/nuclear/.cmake-format.py +++ b/src/nuclear/.cmake-format.py @@ -4,9 +4,7 @@ with section("parse"): # Specify structure for custom cmake functions - additional_commands = { - "HeaderLibrary": {"kwargs": {"NAME": "*", "HEADER": "*", "PATH_SUFFIX": "*", "URL": "*"}}, - } + additional_commands = {} # Specify variable tags. vartags = [] @@ -213,6 +211,5 @@ # A dictionary containing any per-command configuration overrides. Currently # only `command_case` is supported. per_command = { - "HeaderLibrary": {"command_case": "unchanged"}, "ToolchainLibraryFinder": {"command_case": "unchanged"}, } diff --git a/src/nuclear/.github/workflows/gcc.yaml b/src/nuclear/.github/workflows/gcc.yaml index 4bec64c0..d1200508 100644 --- a/src/nuclear/.github/workflows/gcc.yaml +++ b/src/nuclear/.github/workflows/gcc.yaml @@ -34,10 +34,6 @@ jobs: version: "8" - container: ubuntu:20.04 version: "7" - - container: ubuntu:18.04 - version: "6" - - container: ubuntu:18.04 - version: "5" name: Linux GCC-${{ matrix.toolchain.version }} runs-on: ubuntu-latest @@ -48,12 +44,12 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Update for all the actions that need to install stuff - run: | apt-get update - apt-get install -y software-properties-common + apt-get install -y software-properties-common unzip - name: Install GCC run: | @@ -95,7 +91,15 @@ jobs: run: ccache --show-stats - name: Test - timeout-minutes: 10 - run: | - build/tests/test_nuclear - for f in build/tests/individual/*; do echo "Testing $f"; ./$f; done + timeout-minutes: 2 + working-directory: build/tests + run: ctest --output-on-failure -E "dsl/UDP" + + - name: Upload Traces + if: always() + uses: actions/upload-artifact@v4 + with: + name: traces-gcc-${{ matrix.toolchain.version }} + path: build/tests/**/*.trace + retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day) + overwrite: true diff --git a/src/nuclear/.github/workflows/linting.yaml b/src/nuclear/.github/workflows/linting.yaml index b7f93371..bacca550 100644 --- a/src/nuclear/.github/workflows/linting.yaml +++ b/src/nuclear/.github/workflows/linting.yaml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install clang-tidy-15 run: | @@ -48,6 +48,34 @@ jobs: key: ${{ github.job }} max-size: 100M + # Install ctcache + - name: Install ctcache + shell: bash + run: | + CTCACHE_REF=debfea68152c5221d8f409cbef85dc5d0f98071d + curl --location https://raw.githubusercontent.com/matus-chochlik/ctcache/${CTCACHE_REF}/clang-tidy-cache | sudo tee /usr/local/bin/clang-tidy-cache > /dev/null + + echo #!/bin/bash | sudo tee /usr/local/bin/clang-tidy > /dev/null + echo /usr/local/bin/clang-tidy-cache '"${CTCACHE_CLANG_TIDY}"' '"$@"' | sudo tee -a /usr/local/bin/clang-tidy > /dev/null + + sudo chmod +x /usr/local/bin/clang-tidy-cache /usr/local/bin/clang-tidy + + mkdir -p ${{ github.workspace }}/.ctcache + + echo CTCACHE_CLANG_TIDY='/usr/bin/clang-tidy-15' >> "$GITHUB_ENV" + echo CTCACHE_LOCAL=1 >> "$GITHUB_ENV" + echo CTCACHE_SAVE_OUTPUT=1 >> "$GITHUB_ENV" + echo CTCACHE_DIR='${{github.workspace}}/.ctcache' >> "$GITHUB_ENV" + echo "CTCACHE_NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")" >> $GITHUB_ENV + + - name: Setup caching for ctcache + uses: actions/cache@v4 + with: + key: ctcache-${{ github.workflow }}-${{ github.job }}-${{ env.CTCACHE_NOW }} + path: ${{ env.CTCACHE_DIR }} + restore-keys: ctcache-${{ github.workflow }}-${{ github.job }}- + save-always: true + - name: Configure CMake run: | cmake -E make_directory build @@ -56,14 +84,16 @@ jobs: -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DBUILD_TESTS=ON \ - -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_BUILD_TYPE=Debug \ -DCI_BUILD=ON \ -DENABLE_CLANG_TIDY=ON - name: Build timeout-minutes: 30 - # Execute the build. You can specify a specific target with "--target " - run: cmake --build build --config Release --parallel 2 + run: cmake --build build --config Debug --parallel 2 - name: CCache Stats run: ccache --show-stats + + - name: CTCache Stats + run: clang-tidy-cache --show-stats diff --git a/src/nuclear/.github/workflows/macos.yaml b/src/nuclear/.github/workflows/macos.yaml index c9028379..cc949da1 100644 --- a/src/nuclear/.github/workflows/macos.yaml +++ b/src/nuclear/.github/workflows/macos.yaml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup CCache uses: hendrikmuhs/ccache-action@v1.2 @@ -38,6 +38,8 @@ jobs: ninjaVersion: 1.11.1 - name: Configure CMake + env: + CXXFLAGS: -DNUCLEAR_TEST_TIME_UNIT_DEN=10 run: | cmake -E make_directory build cmake -S . -B build \ @@ -57,7 +59,15 @@ jobs: run: ccache --show-stats - name: Test - timeout-minutes: 10 - run: | - build/tests/test_nuclear - for f in build/tests/individual/*; do echo "Testing $f"; ./$f; done + timeout-minutes: 5 + working-directory: build/tests + run: ctest --output-on-failure + + - name: Upload Traces + if: always() + uses: actions/upload-artifact@v4 + with: + name: traces-macos + path: build/tests/**/*.trace + retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day) + overwrite: true diff --git a/src/nuclear/.github/workflows/sonarcloud.yaml b/src/nuclear/.github/workflows/sonarcloud.yaml index 9f0a1d84..3a02f51e 100644 --- a/src/nuclear/.github/workflows/sonarcloud.yaml +++ b/src/nuclear/.github/workflows/sonarcloud.yaml @@ -22,15 +22,15 @@ jobs: env: BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Install gcovr - run: pip install gcovr==6.0 + run: pip install gcovr==7.2 - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@v2 + uses: SonarSource/sonarcloud-github-c-cpp@v3 - name: Install CMake uses: lukka/get-cmake@latest @@ -57,24 +57,34 @@ jobs: -DCI_BUILD=ON \ -DENABLE_CLANG_TIDY=OFF - - name: Build with Sonar Wrapper + - name: Build the code in debug mode timeout-minutes: 30 - run: build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Debug + run: cmake --build build/ --config Debug - name: Run tests to generate coverage statistics timeout-minutes: 10 - run: | - build/tests/test_nuclear - for f in build/tests/individual/*; do echo "Testing $f"; ./$f; done + working-directory: build/tests + run: ctest --output-on-failure - name: Collect coverage into one XML report - run: gcovr --exclude-unreachable-branches --exclude-noncode-lines --sonarqube > coverage.xml + if: always() + run: gcovr --gcov-ignore-parse-errors=negative_hits.warn_once_per_file --exclude-unreachable-branches --exclude-noncode-lines --sonarqube > coverage.xml - name: Run sonar-scanner + if: always() env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | sonar-scanner \ - --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ + --define sonar.cfamily.compile-commands=build/compile_commands.json \ --define sonar.coverageReportPaths=coverage.xml + + - name: Upload Traces + if: always() + uses: actions/upload-artifact@v4 + with: + name: traces-sonar + path: build/tests/**/*.trace + retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day) + overwrite: true diff --git a/src/nuclear/.github/workflows/windows.yaml b/src/nuclear/.github/workflows/windows.yaml index 500b9ea7..2163cc35 100644 --- a/src/nuclear/.github/workflows/windows.yaml +++ b/src/nuclear/.github/workflows/windows.yaml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup CCache uses: hendrikmuhs/ccache-action@v1.2 @@ -56,6 +56,8 @@ jobs: uses: ilammy/msvc-dev-cmd@v1 - name: Configure CMake + env: + CXXFLAGS: -DNUCLEAR_TEST_TIME_UNIT_DEN=10 shell: cmd run: | cmake -E make_directory build @@ -78,8 +80,15 @@ jobs: run: sccache --show-stats - name: Test - timeout-minutes: 10 - shell: bash - run: | - build/tests/test_nuclear.exe - for f in build/tests/individual/*; do echo "Testing $f"; ./$f; done + timeout-minutes: 5 + working-directory: build/tests + run: ctest --output-on-failure + + - name: Upload Traces + if: always() + uses: actions/upload-artifact@v4 + with: + name: traces-windows-${{ matrix.toolchain.name }} + path: build/tests/**/*.trace + retention-days: 1 # This sets the artifact TTL to 1 day (minimum is 1 day) + overwrite: true diff --git a/src/nuclear/.vscode/settings.json b/src/nuclear/.vscode/settings.json index b4761bf5..42535815 100644 --- a/src/nuclear/.vscode/settings.json +++ b/src/nuclear/.vscode/settings.json @@ -12,86 +12,8 @@ "editor.rulers": [120], "cmake.configureOnOpen": true, "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools", - "files.associations": { - "*.ipp": "cpp", - "__bit_reference": "cpp", - "__bits": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__errc": "cpp", - "__hash_table": "cpp", - "__locale": "cpp", - "__mutex_base": "cpp", - "__node_handle": "cpp", - "__nullptr": "cpp", - "__split_buffer": "cpp", - "__string": "cpp", - "__threading_support": "cpp", - "__tree": "cpp", - "__tuple": "cpp", - "array": "cpp", - "atomic": "cpp", - "bit": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "chrono": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "compare": "cpp", - "complex": "cpp", - "concepts": "cpp", - "condition_variable": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "exception": "cpp", - "fstream": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "memory": "cpp", - "mutex": "cpp", - "new": "cpp", - "numeric": "cpp", - "optional": "cpp", - "ostream": "cpp", - "queue": "cpp", - "random": "cpp", - "ratio": "cpp", - "regex": "cpp", - "set": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "tuple": "cpp", - "type_traits": "cpp", - "typeindex": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "variant": "cpp", - "vector": "cpp", - "algorithm": "cpp", - "__verbose_abort": "cpp", - "thread": "cpp" + "sonarlint.connectedMode.project": { + "connectionId": "fastcode", + "projectKey": "Fastcode_NUClear" } } diff --git a/src/nuclear/CMakeLists.txt b/src/nuclear/CMakeLists.txt index e5e0299a..b5064757 100644 --- a/src/nuclear/CMakeLists.txt +++ b/src/nuclear/CMakeLists.txt @@ -30,7 +30,13 @@ project( ) # We use additional modules that cmake needs to know about -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/" "${CMAKE_SOURCE_DIR}/cmake/Modules/") + +# Output the compilation database +set(CMAKE_EXPORT_COMPILE_COMMANDS + ON + CACHE STRING "Enable/Disable output of compile commands during generation." FORCE +) # Set a default build type if none was specified set(default_build_type "Release") @@ -57,37 +63,13 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(MASTER_PROJECT ON) endif() -if(MSVC) - add_compile_options(/W4) -else() - add_compile_options(-Wall -Wextra -pedantic) -endif(MSVC) - # If this option is set we are building using continous integration option(CI_BUILD "Enable build options for building in the CI server" OFF) -# Default not to run the clang-tidy checks, default to whatever our CI_BUILD is -option(ENABLE_CLANG_TIDY "Enable building with clang-tidy checks.") -if(ENABLE_CLANG_TIDY) - # Search for clang-tidy-15 first as this is the version installed in CI - find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy-15 clang-tidy) - if(NOT CLANG_TIDY_EXECUTABLE) - message(FATAL_ERROR "clang-tidy-15 not found.") - endif() - - # Report clang-tidy executable details - execute_process(COMMAND "${CLANG_TIDY_EXECUTABLE}" "--version" OUTPUT_VARIABLE CLANG_TIDY_VERSION) - string(REGEX REPLACE ".*LLVM version ([0-9.]*).*" "\\1" CLANG_TIDY_VERSION "${CLANG_TIDY_VERSION}") - message(STATUS "Found clang-tidy: ${CLANG_TIDY_EXECUTABLE} ${CLANG_TIDY_VERSION}") - - # Build clang-tidy command line - set(CLANG_TIDY_ARGS "${CLANG_TIDY_EXECUTABLE}" "--use-color" "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy") - if(CI_BUILD) - set(CLANG_TIDY_ARGS "${CLANG_TIDY_EXECUTABLE}" "-warnings-as-errors=*") - endif(CI_BUILD) - set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}" "--extra-arg=-std=c++14") - set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY_ARGS}" "--extra-arg=-std=c99") -endif(ENABLE_CLANG_TIDY) +# Include files to set up the compiler options +include(ClangTidy) +include(CompilerOptions) +include(Sanitizers) # If we are doing a CI build then we want to enable -Werror when compiling warnings are bad. We will also make it fail # if clang-tidy has an error @@ -99,19 +81,18 @@ if(CI_BUILD) endif() endif(CI_BUILD) -# Make the compiler display colours always (even when we build with ninja) -if(CMAKE_CXX_COMPILER_ID MATCHES GNU) - add_compile_options(-fdiagnostics-color=always) -endif() -if(CMAKE_CXX_COMPILER_ID MATCHES Clang) - add_compile_options(-fcolor-diagnostics) -endif() - # Add the src directory add_subdirectory(src) # Add the tests directory -add_subdirectory(tests) +option(BUILD_TESTS "Builds all of the NUClear unit tests." ON) +if(BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() # Add the documentation subdirectory -add_subdirectory(docs) +option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen and Sphinx)" OFF) +if(BUILD_DOCUMENTATION) + add_subdirectory(docs) +endif() diff --git a/src/nuclear/cmake/ClangTidy.cmake b/src/nuclear/cmake/ClangTidy.cmake new file mode 100644 index 00000000..e1d6f051 --- /dev/null +++ b/src/nuclear/cmake/ClangTidy.cmake @@ -0,0 +1,22 @@ +# Default not to run the clang-tidy checks, default to whatever our CI_BUILD is +option(ENABLE_CLANG_TIDY "Enable building with clang-tidy checks.") +if(ENABLE_CLANG_TIDY) + # Search for clang-tidy-15 first as this is the version installed in CI + find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy-15 clang-tidy) + if(NOT CLANG_TIDY_EXECUTABLE) + message(FATAL_ERROR "clang-tidy-15 not found.") + endif() + + # Report clang-tidy executable details + execute_process(COMMAND "${CLANG_TIDY_EXECUTABLE}" "--version" OUTPUT_VARIABLE CLANG_TIDY_VERSION) + string(REGEX REPLACE ".*LLVM version ([0-9.]*).*" "\\1" CLANG_TIDY_VERSION "${CLANG_TIDY_VERSION}") + message(STATUS "Found clang-tidy: ${CLANG_TIDY_EXECUTABLE} ${CLANG_TIDY_VERSION}") + + # Build clang-tidy command line + set(CLANG_TIDY_ARGS "${CLANG_TIDY_EXECUTABLE}" "--use-color" "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy") + if(CI_BUILD) + set(CLANG_TIDY_ARGS "${CLANG_TIDY_EXECUTABLE}" "-warnings-as-errors=*") + endif(CI_BUILD) + set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}" "--extra-arg=-std=c++14") + set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY_ARGS}" "--extra-arg=-std=c99") +endif(ENABLE_CLANG_TIDY) diff --git a/src/nuclear/cmake/CompilerOptions.cmake b/src/nuclear/cmake/CompilerOptions.cmake new file mode 100644 index 00000000..4f8bcde7 --- /dev/null +++ b/src/nuclear/cmake/CompilerOptions.cmake @@ -0,0 +1,7 @@ +# Make the compiler display colours always (even when we build with ninja) +if(CMAKE_CXX_COMPILER_ID MATCHES GNU) + add_compile_options(-fdiagnostics-color=always) +endif() +if(CMAKE_CXX_COMPILER_ID MATCHES Clang) + add_compile_options(-fcolor-diagnostics) +endif() diff --git a/src/nuclear/cmake/Modules/FindCATCH.cmake b/src/nuclear/cmake/Modules/FindCATCH.cmake deleted file mode 100644 index 4f6181c3..00000000 --- a/src/nuclear/cmake/Modules/FindCATCH.cmake +++ /dev/null @@ -1,29 +0,0 @@ -#[[ -MIT License - -Copyright (c) 2013 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. -]] - -include(HeaderLibrary) - -HeaderLibrary( - NAME CATCH - HEADER catch.hpp - URL "https://raw.githubusercontent.com/catchorg/Catch2/v2.x/single_include/catch2/catch.hpp" -) diff --git a/src/nuclear/cmake/Modules/HeaderLibrary.cmake b/src/nuclear/cmake/Modules/HeaderLibrary.cmake deleted file mode 100644 index 6fd31852..00000000 --- a/src/nuclear/cmake/Modules/HeaderLibrary.cmake +++ /dev/null @@ -1,93 +0,0 @@ -#[[ -MIT License - -Copyright (c) 2013 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. -]] - -include(CMakeParseArguments) -function(HeaderLibrary) - # Extract the arguments from our function call - set(options, "") - set(oneValueArgs "NAME") - set(multiValueArgs "HEADER" "PATH_SUFFIX" "URL") - cmake_parse_arguments(PACKAGE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Clear our required_vars variable - unset(required_vars) - set(OUTPUT_DIR "${CMAKE_BINARY_DIR}/include") - - # Find our include path - find_path( - "${PACKAGE_NAME}_INCLUDE_DIR" - NAMES ${PACKAGE_HEADER} - DOC "The ${PACKAGE_NAME} include directory" - PATHS "${OUTPUT_DIR}" - PATH_SUFFIXES ${PACKAGE_PATH_SUFFIX} - ) - - # File doesn't exist in standard search paths, download it - if(NOT ${PACKAGE_NAME}_INCLUDE_DIR) - - # Create the output folder if it doesn't already exist - if(NOT EXISTS "${OUTPUT_DIR}") - file(MAKE_DIRECTORY "${OUTPUT_DIR}") - endif() - - # Download file. - file(DOWNLOAD "${PACKAGE_URL}" "${OUTPUT_DIR}/${PACKAGE_HEADER}" STATUS ${PACKAGE_NAME}_STATUS) - - list(GET ${PACKAGE_NAME}_STATUS 0 ${PACKAGE_NAME}_STATUS_CODE) - list(GET ${PACKAGE_NAME}_STATUS 1 ${PACKAGE_NAME}_ERROR_STRING) - - # Parse download status - if(${PACKAGE_NAME}_STATUS_CODE EQUAL 0) - message(STATUS "Successfully downloaded ${PACKAGE_NAME} library.") - - set(${PACKAGE_NAME}_INCLUDE_DIR "${OUTPUT_DIR}") - - else() - message(ERROR "Failed to download ${PACKAGE_NAME} library. Error: ${${PACKAGE_NAME}_ERROR_STRING}") - file(REMOVE "${OUTPUT_DIR}/${PACKAGE_HEADER}") - endif() - endif() - - # Setup and export our variables - set(required_vars ${required_vars} "${PACKAGE_NAME}_INCLUDE_DIR") - set(${PACKAGE_NAME}_INCLUDE_DIRS - ${${PACKAGE_NAME}_INCLUDE_DIR} - PARENT_SCOPE - ) - mark_as_advanced(${PACKAGE_NAME}_INCLUDE_DIR ${PACKAGE_NAME}_INCLUDE_DIRS) - - # Find the package - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args( - ${PACKAGE_NAME} - FOUND_VAR ${PACKAGE_NAME}_FOUND - REQUIRED_VARS ${required_vars} - VERSION_VAR ${PACKAGE_NAME}_VERSION - ) - - # Export our found variable to parent scope - set(${PACKAGE_NAME}_FOUND - ${PACKAGE_NAME}_FOUND - PARENT_SCOPE - ) - -endfunction(HeaderLibrary) diff --git a/src/nuclear/cmake/Sanitizers.cmake b/src/nuclear/cmake/Sanitizers.cmake new file mode 100644 index 00000000..91877a30 --- /dev/null +++ b/src/nuclear/cmake/Sanitizers.cmake @@ -0,0 +1,47 @@ +############################################################################### +### Options for enabling different sanitisers. ### +### ### +### User beware: ### +### Not all sanitisers can be enabled at the same time. ### +############################################################################### +option(USE_ASAN "Enable address sanitization" OFF) +if(USE_ASAN) + add_compile_options(-fsanitize=address -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=address) + link_libraries(asan) +endif(USE_ASAN) + +option(USE_LSAN "Enable leak sanitization" OFF) +if(USE_LSAN) + add_compile_options(-fsanitize=leak -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=leak) + link_libraries(lsan) +endif(USE_LSAN) + +option(USE_TSAN "Enable thread sanitization" OFF) +if(USE_TSAN) + add_compile_options(-fsanitize=thread -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=thread) + link_libraries(tsan) +endif(USE_TSAN) + +option(USE_UBSAN "Enable undefined behaviour sanitization" OFF) +if(USE_UBSAN) + add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer -U_FORTIFY_SOURCE -fno-common) + add_link_options(-fsanitize=undefined) + link_libraries(ubsan) +endif(USE_UBSAN) + +# Option for enabling code profiling. Disabled by default +option(ENABLE_PROFILING "Compile with profiling support enabled." OFF) +if(ENABLE_PROFILING) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + message( + WARNING + "Profiling is enabled but no debugging symbols will be kept in the compiled binaries. This may cause fine-grained profilling data to be lost." + ) + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg -fprofile-arcs") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -fprofile-arcs") + set(CMAKE_LINKER "${CMAKE_LINKER_FLAGS} -pg -fprofile-arcs") +endif(ENABLE_PROFILING) diff --git a/src/nuclear/docs/CMakeLists.txt b/src/nuclear/docs/CMakeLists.txt index 41ace683..6e521352 100644 --- a/src/nuclear/docs/CMakeLists.txt +++ b/src/nuclear/docs/CMakeLists.txt @@ -19,24 +19,19 @@ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEM 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. ]] -option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen and Sphinx)" FALSE) -if(BUILD_DOCUMENTATION) - - find_package(Doxygen REQUIRED) - find_package(Sphinx REQUIRED) - - add_custom_target( - docs - COMMAND ${Sphinx_BINARY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating documentation pages using sphinx" - ) - - install( - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DESTINATION share/doc - OPTIONAL - ) - -endif(BUILD_DOCUMENTATION) +find_package(Doxygen REQUIRED) +find_package(Sphinx REQUIRED) + +add_custom_target( + docs + COMMAND ${Sphinx_BINARY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation pages using sphinx" +) + +install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DESTINATION share/doc + OPTIONAL +) diff --git a/src/nuclear/docs/dsl.rst b/src/nuclear/docs/dsl.rst index a5fd140a..e186947b 100644 --- a/src/nuclear/docs/dsl.rst +++ b/src/nuclear/docs/dsl.rst @@ -1,4 +1,4 @@ -include .special.rst +.. include:: .special.rst NUClear DSL @@ -10,18 +10,14 @@ On Statements On statements are used by :ref:`Reactors` wishing to make subscriptions to the :ref:`PowerPlant`. Using this statement, developers can set the conditions under which desired :ref:`Reactions` will run. -anatomy of an on statement +Anatomy of an on statement -------------------------- The on statement can be seen as containing three main parts. The DSL Request, the Runtime Arguments, and the Callback. -.. raw:: html - - On<...>(Runtime, ... ).then(function); -

- DSL Request
- -This is :red:`red !` And :blue:`this part is blue`. +:blue:`On`\(:red:`Runtime, Args`).then(:green:`CallbackFunction`); +:blue:`DSL Request` +------------------- The DSL request can be fused together through any combination of DSL words. The combination of these words will define the kind of reaction which is being requested (for example, :ref:`Trigger` will define a reaction that should occur when a required data type is emitted, while :ref:`Every` will define periodic reactions). @@ -30,22 +26,18 @@ For reactions to occur, at least one Binding DSL word should be present in the D those which are binding are: :ref:`Trigger`, :ref:`With`, :ref:`Every`, :ref:`Always`, :ref:`Startup`, :ref:`Shutdown`, :ref:`TCP`, :ref:`UDP` and :ref:`Network` -.. raw:: html - - Runtime Arguments - +:red:`Runtime Arguments` +------------------------ Some DSL words will provide the ability to make changes to the system during runtime. This means that NUClear avoids the need for a system restart should a configuration, port number, or file need to be changed while the system is running. From the provided DSL words, those which take runtime arguments are: :ref:`IO`, :ref:`TCP`, and :ref:`UDP` -.. raw:: html - - Callback - +:green:`Callback` +----------------- Finally, the developer can define the callback which will execute when the reaction is triggered during runtime. The -callback can be defined using a C++ lambda function. +callback can be defined using a C++ lambda expression. During system runtime, the argument selection for the callback works on the principle of fission, in that the arguments provided with the callback can be deduced as needed. For example: @@ -142,7 +134,7 @@ Always .. doxygenstruct:: NUClear::dsl::word::Always Watchdog -````````` +```````` .. doxygenstruct:: NUClear::dsl::word::Watchdog @@ -195,21 +187,16 @@ Note that data can be emitted under varying scopes: Local Emitting -------------- -These emissions send data to the local instance of the NUClear powerplant. There are a number of scopes under which +These emissions send data to the local instance of the NUClear PowerPlant. There are a number of scopes under which these emissions can take place: -.. todo:: - - Trent - I need to decide and get consistent on what we will call the powerPlant. Should it be PowerPlant or - powerPlant - what will you prefer - Scope::LOCAL ```````````` .. doxygenstruct:: NUClear::dsl::word::emit::Local -Scope::DIRECT +Scope::INLINE ````````````` -.. doxygenstruct:: NUClear::dsl::word::emit::Direct +.. doxygenstruct:: NUClear::dsl::word::emit::Inline Scope::Initialise `````````````````` diff --git a/src/nuclear/docs/extension.rst b/src/nuclear/docs/extension.rst index f5b2f6ce..55ff8d12 100644 --- a/src/nuclear/docs/extension.rst +++ b/src/nuclear/docs/extension.rst @@ -31,9 +31,9 @@ passed in. It is important to note that the type will only be considered by NUCl attributes need to be stored in the DSL word type template it and use static variables, see `Sync`. There are DSL words that are not meant to be used directly but as a part of other words, see `CacheGet` and `TypeBind`. -`TypeBind` adds the reaction to the list of reactions to be run when a `Local` or `Direct` emit is called for the data +`TypeBind` adds the reaction to the list of reactions to be run when a `Local` or `Inline` emit is called for the data type. `CacheGet` gets the last value from a thread-local cache (see `ThreadSore` below) this cache is usually populated -in the last a `Local` or `Direct` emit call for the data type. +in the last a `Local` or `Inline` emit call for the data type. If the type you want to become a DSL extension word is not defined within your control specialise `DSLProxy<>` with the type. Provide the template methods to the specialisation of `DSLProxy<>` as if it were the type. @@ -43,7 +43,7 @@ Bind .. codeblock:: c++ template - static inline void bind(const std::shared_ptr& reaction, /*More arguments can be declared*/) + static void bind(const std::shared_ptr& reaction, /*More arguments can be declared*/) This function is called when the reaction is bound, it should be thought of as the constructor. It is used to setup anything that is required by the DSL word. @@ -56,7 +56,7 @@ destructor. e.g. for the `IO` word we have .. codeblock:: c++ reaction->unbinders.push_back([](const threading::Reaction& r) { - r.reactor.emit(std::make_unique>(r.id)); + r.reactor.emit(std::make_unique>(r.id)); }); which will tell the extension reactor that this reaction no longer exists. @@ -68,7 +68,7 @@ Get .. codeblock:: c++ template - static inline T get(threading::Reaction&) + static T get(threading::ReactionTask& task) This is used to get the data for the callback. The returned value is passed to the callback. @@ -84,7 +84,7 @@ Precondition .. codeblock:: c++ template - static inline bool precondition(threading::Reaction& reaction) + static bool precondition(threading::ReactionTask& task) A precondition is used to test if the reaction should run. On a true return the reaction will run as normal. On a false return the reaction will be dropped. @@ -103,7 +103,7 @@ Reschedule .. codeblock:: c++ template - static inline std::unique_ptr reschedule(std::unique_ptr&& task) + static std::unique_ptr reschedule(std::unique_ptr&& task) The ownership of the reaction task is passed to the DSL word. The task returned will be run instead of the passed in reaction task. If the returned task is the one passed in the task will be run normally. @@ -118,7 +118,7 @@ Transient .. codeblock:: c++ template <> - struct is_transient : public std::true_type {}; + struct is_transient : std::true_type {}; When the data returned from a `get` is falsy and its type is marked transient the latest truthy data from the `get` return is instead used. If the data is falsy and is either not marked transient or nothing truthy has yet been returned @@ -156,18 +156,18 @@ The template is used to have multiple static contexts. using task_ptr = std::unique_ptr; - /// @brief our queue which sorts tasks by priority + /// Our queue which sorts tasks by priority static std::priority_queue queue; - /// @brief how many tasks are currently running + /// How many tasks are currently running static volatile bool running; - /// @brief a mutex to ensure data consistency + /// A mutex to ensure data consistency static std::mutex mutex; Now we define the `reschedule` to interrupt any new tasks if we are currently running. Recall that NUClear is multithreaded so a mutex is needed when accessing the static members. .. codeblock:: c++ template - static inline std::unique_ptr reschedule( + static std::unique_ptr reschedule( std::unique_ptr&& task) { // Lock our mutex @@ -218,4 +218,3 @@ We need to instantiate our static members outside the class definition. template std::mutex Sync::mutex; - diff --git a/src/nuclear/docs/networking.rst b/src/nuclear/docs/networking.rst index f80a561a..05d0c8ed 100644 --- a/src/nuclear/docs/networking.rst +++ b/src/nuclear/docs/networking.rst @@ -43,8 +43,8 @@ checks explicitly for an explicit type. Be careful about multiple declarations. For this partial specialisation three static methods need to be defined. .. codeblock:: c++ - static inline std::vector serialise(const T& in) + static std::vector serialise(const T& in) - static inline T deserialise(const std::vector& in) + static T deserialise(const std::vector& in) - static inline uint64_t hash() + static uint64_t hash() diff --git a/src/nuclear/docs/requirements.txt b/src/nuclear/docs/requirements.txt index 65b95c52..b04496ee 100644 --- a/src/nuclear/docs/requirements.txt +++ b/src/nuclear/docs/requirements.txt @@ -1 +1,4 @@ -breathe==4.20 +breathe==4.35.0 +jinja2==3.1.4 +Sphinx>=5.0.0 +sphinx_rtd_theme==1.0.0 diff --git a/src/nuclear/docs/startup.rst b/src/nuclear/docs/startup.rst index 01cc0554..3adc2479 100644 --- a/src/nuclear/docs/startup.rst +++ b/src/nuclear/docs/startup.rst @@ -23,7 +23,7 @@ file for the process. .. code-block:: C++ NUClear::Configuration config; - config.thread_count = 1; + config.default_pool_concurrency = 1; NUClear::PowerPlant plant(config); .. todo:: @@ -63,10 +63,10 @@ Data Emission Statements As the system is single threaded at this time, the order in which reactors are installed can be significantly important. This is pertinent when dealing with any data emissions during reactor construction which are NOT emitted under -:ref:`Scope::Initialise`. For example; data emission during the construction of a reactor using :ref:`Scope::DIRECT`, +:ref:`Scope::Initialise`. For example; data emission during the construction of a reactor using :ref:`Scope::INLINE`, :ref:`Scope::UDP`, or :ref:`Scope::Network` will trigger any necessary activity to run inline. Should any reactions be defined to run as a result of the emission, the task will be generated and also run inline. It is here where the order -in which reactors are installed becomes important. Suppose Reactor1 were to emit under :ref:`Scope::DIRECT`, and +in which reactors are installed becomes important. Suppose Reactor1 were to emit under :ref:`Scope::INLINE`, and Reactor2 had a reaction defined to run on the associated datatype. In this case, the reaction defined by Reactor2 would not run, as it was not yet defined at the time of data emission. However, should the roles be reserved, then the reaction would run. @@ -95,7 +95,7 @@ reaction would run. to use :ref:`Scope::Initialise`. This will put a hold on the data emission, until the next step in the process :ref:`Initialise Scope Tasks`, ensuring that any reactions subscribed to the emission will run. - Anything else?** Emissions during the construction of reactors using :ref:`Scope::DIRECT`, :ref:`Scope::UDP` and + Anything else?** Emissions during the construction of reactors using :ref:`Scope::INLINE`, :ref:`Scope::UDP` and :ref:`Scope::Network` will trigger any reactions (which have already been defined - before the data emission) and force any associated tasks to run inline. @@ -164,7 +164,7 @@ Any on<:ref:`Shutdown`>() reaction requests will then be queued (in the order in :ref:`Priority`::IDLE. Note that during this phase, any other task which would normally be scheduled as a result of a non-direct emission will -be silently dropped, while any tasks which would occur as a result of a :ref:`Scope::DIRECT` emission will interrupt the +be silently dropped, while any tasks which would occur as a result of a :ref:`Scope::INLINE` emission will interrupt the shutdown process and run as normal. .. todo:: @@ -184,7 +184,7 @@ Emissions Scope Table +==========================+=============================================================================================================================================================================================================================================================================================================================================+======================================================================================================================================================================================================================+=====================================================================================================================================================================================================================+ | :ref:`Scope::LOCAL` | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Adds to the queue of tasks to start running when the system shifts to the :ref:`Execution Phase (multithreaded)` | Schedules any tasks for reactions which are bound to the emission data. Adds to the queue of tasks based on the desired :ref:`Priority` level | Any emissions under this scope while the system is in the shutdown phase are ignored. | +--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - | :ref:`Scope::DIRECT` | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the initialization phase, and runs the task in-line. The initialization phase continues upon task completion. | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the task currently executing and runs the new task in-line. The execution phase continues upon task completion. | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the task currently executing and runs the new task in-line. The shutdown phase continues upon task completion. | + | :ref:`Scope::INLINE` | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the initialization phase, and runs the task in-line. The initialization phase continues upon task completion. | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the task currently executing and runs the new task in-line. The execution phase continues upon task completion. | Schedules any tasks for reactions which are currently loaded and bound to the emission data. Pauses the task currently executing and runs the new task in-line. The shutdown phase continues upon task completion. | +--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | :ref:`Scope::Initialise` | Data emitted under this scope during this phase will wait until all reactors have been installed into the powerPlant before triggering any reactions. Any tasks generated as a result of this emission type are the first tasks to run when the powerPlant starts. This is the recommended emission type for this phase of system startup. | Any emissions under this scope while the system is in the execution phase are ignored. | Any emissions under this scope while the system is in the shutdown phase are ignored. | +--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/nuclear/src/CMakeLists.txt b/src/nuclear/src/CMakeLists.txt index 911a429b..9ce7a65a 100644 --- a/src/nuclear/src/CMakeLists.txt +++ b/src/nuclear/src/CMakeLists.txt @@ -41,7 +41,7 @@ target_compile_features(nuclear PUBLIC cxx_std_14) if(MSVC) target_compile_options(nuclear PRIVATE /W4 /WX) else() - target_compile_options(nuclear PRIVATE -Wall -Wextra -pedantic -Werror) + target_compile_options(nuclear PRIVATE -Wall -Wextra -pedantic) endif(MSVC) # Make the NUClearConfig files diff --git a/src/nuclear/src/Configuration.hpp b/src/nuclear/src/Configuration.hpp index c51d678a..c31ef042 100644 --- a/src/nuclear/src/Configuration.hpp +++ b/src/nuclear/src/Configuration.hpp @@ -29,11 +29,12 @@ namespace NUClear { /** - * @brief This class holds the configuration for a PowerPlant. + * This class holds the configuration for a PowerPlant. */ struct Configuration { - /// @brief The number of threads the system will use - size_t thread_count = std::thread::hardware_concurrency() == 0 ? 2 : std::thread::hardware_concurrency(); + /// The number of threads the system will use for the default thread pool + int default_pool_concurrency = + std::thread::hardware_concurrency() == 0 ? 2 : int(std::thread::hardware_concurrency()); }; } // namespace NUClear diff --git a/src/nuclear/src/Environment.hpp b/src/nuclear/src/Environment.hpp index 5bf2d43d..2122fea2 100644 --- a/src/nuclear/src/Environment.hpp +++ b/src/nuclear/src/Environment.hpp @@ -34,13 +34,10 @@ class Reactor; class PowerPlant; /** - * @brief - * Environment defines variables that are passed from the installing PowerPlant context - * into a Reactor. + * Environment defines variables that are passed from the installing PowerPlant context into a Reactor. * - * @details - * The Environment is used to provide information from the PowerPlant to Reactors. - * Each Reactor owns it's own environment and can use it to access useful information. + * The Environment is used to provide information from the PowerPlant to Reactors. + * Each Reactor owns it's own environment and can use it to access useful information. */ class Environment { public: @@ -51,9 +48,9 @@ class Environment { friend class PowerPlant; friend class Reactor; - /// @brief The PowerPlant to use in this reactor + /// The PowerPlant to use in this reactor PowerPlant& powerplant; - /// @brief The name of the reactor + /// The name of the reactor std::string reactor_name; }; diff --git a/src/nuclear/src/LogLevel.cpp b/src/nuclear/src/LogLevel.cpp new file mode 100644 index 00000000..e7dd3813 --- /dev/null +++ b/src/nuclear/src/LogLevel.cpp @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2013 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. + */ + +#include "LogLevel.hpp" + +#include + +namespace NUClear { + +std::string to_string(const LogLevel& level) { + switch (level) { + case LogLevel::TRACE: return "TRACE"; + case LogLevel::DEBUG: return "DEBUG"; + case LogLevel::INFO: return "INFO"; + case LogLevel::WARN: return "WARN"; + case LogLevel::ERROR: return "ERROR"; + case LogLevel::FATAL: return "FATAL"; + default: + case LogLevel::UNKNOWN: return "UNKNOWN"; + } +} + +LogLevel from_string(const std::string& level) { + return 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; +} + +std::ostream& operator<<(std::ostream& os, const LogLevel& level) { + return os << to_string(level); +} + +} // namespace NUClear diff --git a/src/nuclear/src/LogLevel.hpp b/src/nuclear/src/LogLevel.hpp index ebf1494c..148a4e14 100644 --- a/src/nuclear/src/LogLevel.hpp +++ b/src/nuclear/src/LogLevel.hpp @@ -23,6 +23,8 @@ #ifndef NUCLEAR_LOGLEVEL_HPP #define NUCLEAR_LOGLEVEL_HPP +#include + // Why do we need to include platform.hpp here? // Because windows defines a bunch of things for legacy reasons, one of which is a #define for ERROR as blank // Of course this causes a problem when we define our own token below as error as the preprocessor removes it @@ -32,16 +34,14 @@ namespace NUClear { /** - * @brief LogLevel defines the different levels log messages can be set to. + * LogLevel defines the different levels log messages can be set to. * - * @details - * 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. + * 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 { +enum LogLevel : uint8_t { /** - * @brief - * Don't use this log level when emitting logs, it is for setting reactor log level from non reactor sources. + * 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. @@ -49,70 +49,83 @@ enum LogLevel { UNKNOWN, /** - * @brief - * The Trace level contains messages that are used to trace the exact flow of execution. + * The Trace level contains messages that are used to trace the exact flow of execution. * - * @details - * This level is extremely verbose and often has a message per line of code. + * This level is extremely verbose and often has a message per line of code. */ TRACE, /** - * @brief - * Debug contains messages that represent the inputs and outputs of different - * computation units. + * Debug contains messages that represent the inputs and outputs of different computation units. * - * @details - * 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. + * 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, /** - * @brief - * The info level is used to provide high level goal messages such as function start - * or successful completion. + * The info level is used to provide high level goal messages such as function start or successful completion. * - * @details - * This shows when key user-facing functionality is executed and tells us that everything - * is working without getting into the details. + * This shows when key user-facing functionality is executed and tells us that everything is working without getting + * into the details. */ INFO, /** - * @brief The warning level is used to notify us that everything might not be working perfectly. + * The warning level is used to notify us that everything might not be working perfectly. * - * @details - * Warnings are errors or inconsistencies that aren't fatal and generally do not completely - * break the system. However a warning message should require action from someone and should - * point to a section of the system that needs attention. + * 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, /** - * @brief - * The error level is used to report unexpected behavior. + * The error level is used to report unexpected behavior. - * @details - * 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. + * 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, /** - * @brief Fatal is a program destroying error that needs to be addressed immediately. + * Fatal is a program destroying error that needs to be addressed immediately. * - * @details - * 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. + * 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 }; +/** + * 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/nuclear/src/PowerPlant.cpp b/src/nuclear/src/PowerPlant.cpp index 4e20eb7e..89f30ea6 100644 --- a/src/nuclear/src/PowerPlant.cpp +++ b/src/nuclear/src/PowerPlant.cpp @@ -22,11 +22,53 @@ #include "PowerPlant.hpp" +#include +#include + +#include "Reactor.hpp" +#include "dsl/store/DataStore.hpp" +#include "dsl/word/Shutdown.hpp" +#include "dsl/word/Startup.hpp" +#include "dsl/word/emit/Inline.hpp" +#include "extension/ChronoController.hpp" +#include "extension/IOController.hpp" +#include "extension/NetworkController.hpp" +#include "message/CommandLineArguments.hpp" +#include "message/LogMessage.hpp" +#include "threading/ReactionTask.hpp" + namespace NUClear { +namespace util { + struct GroupDescriptor; + struct ThreadPoolDescriptor; +} // namespace util // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) PowerPlant* PowerPlant::powerplant = nullptr; +// This is taking argc and argv as given by main so this should not take an array +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) +PowerPlant::PowerPlant(Configuration config, int argc, const char* argv[]) + : scheduler(config.default_pool_concurrency) { + + // Stop people from making more then one powerplant + if (powerplant != nullptr) { + throw std::runtime_error("There is already a powerplant in existence (There should be a single PowerPlant)"); + } + + // Store our static variable + powerplant = this; + + // Emit our arguments if any. + message::CommandLineArguments args; + for (int i = 0; i < argc; ++i) { + args.emplace_back(argv[i]); + } + + // Emit the command line arguments so they are available for any With clauses. + emit(std::make_unique(args)); +} + PowerPlant::~PowerPlant() { // Make sure reactors are destroyed before anything else while (!reactors.empty()) { @@ -39,56 +81,50 @@ PowerPlant::~PowerPlant() { void PowerPlant::start() { - // We are now running - is_running.store(true); - - // Direct emit startup event and command line arguments - emit(std::make_unique()); - emit_shared(dsl::store::DataStore::get()); + // Inline emit startup event and command line arguments + emit(std::make_unique()); + emit_shared(dsl::store::DataStore::get()); // Start all of the threads scheduler.start(); } -void PowerPlant::submit(const NUClear::id_t& id, - const int& priority, - const util::GroupDescriptor& group, - const util::ThreadPoolDescriptor& pool, - const bool& immediate, - std::function&& task) { - scheduler.submit(id, priority, group, pool, immediate, std::move(task)); +void PowerPlant::add_idle_task(const std::shared_ptr& reaction, + const std::shared_ptr& pool_descriptor) { + scheduler.add_idle_task(reaction, pool_descriptor); } -void PowerPlant::submit(std::unique_ptr&& task, const bool& immediate) noexcept { - // Only submit non null tasks - if (task) { - try { - const std::shared_ptr t(std::move(task)); - submit(t->id, t->priority, t->group_descriptor, t->thread_pool_descriptor, immediate, [t]() { t->run(); }); - } - catch (const std::exception& ex) { - task->parent.reactor.log("There was an exception while submitting a reaction", ex.what()); - } - catch (...) { - task->parent.reactor.log("There was an unknown exception while submitting a reaction"); - } - } +void PowerPlant::remove_idle_task(const NUClear::id_t& id, + const std::shared_ptr& pool_descriptor) { + scheduler.remove_idle_task(id, pool_descriptor); +} + +void PowerPlant::submit(std::unique_ptr&& task) noexcept { + scheduler.submit(std::move(task)); } -void PowerPlant::shutdown() { +void PowerPlant::log(const LogLevel& level, std::string message) { + // Get the current task + const auto* current_task = threading::ReactionTask::get_current_task(); - // Stop running before we emit the Shutdown event - // Some things such as on depend on this flag and it's possible to miss it - is_running.store(false); + // Inline emit the log message to default handlers to pause the current task until the log message is processed + emit(std::make_unique( + level, + current_task != nullptr ? current_task->parent->reactor.log_level : LogLevel::UNKNOWN, + std::move(message), + current_task != nullptr ? current_task->statistics : nullptr)); +} +void PowerPlant::log(const LogLevel& level, std::stringstream& message) { + log(level, message.str()); +} + +void PowerPlant::shutdown(bool force) { // Emit our shutdown event emit(std::make_unique()); // Shutdown the scheduler - scheduler.shutdown(); + scheduler.stop(force); } -bool PowerPlant::running() const { - return is_running.load(); -} } // namespace NUClear diff --git a/src/nuclear/src/PowerPlant.hpp b/src/nuclear/src/PowerPlant.hpp index ca180748..c7dc6969 100644 --- a/src/nuclear/src/PowerPlant.hpp +++ b/src/nuclear/src/PowerPlant.hpp @@ -20,65 +20,82 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef NUCLEAR_POWERPLANT_HPP -#define NUCLEAR_POWERPLANT_HPP +#ifndef NUCLEAR_POWER_PLANT_HPP +#define NUCLEAR_POWER_PLANT_HPP #include -#include -#include +#include #include -#include -#include -#include #include -#include -#include -#include +#include #include // Utilities #include "Configuration.hpp" +#include "Environment.hpp" #include "LogLevel.hpp" #include "id.hpp" -#include "message/LogMessage.hpp" #include "threading/ReactionTask.hpp" -#include "threading/TaskScheduler.hpp" +#include "threading/scheduler/Scheduler.hpp" #include "util/FunctionFusion.hpp" #include "util/demangle.hpp" -#include "util/main_thread_id.hpp" -#include "util/unpack.hpp" namespace NUClear { -// Forward declare reactor +// Forward declarations class Reactor; +namespace util { + struct ThreadPoolDescriptor; +} // namespace util +namespace dsl { + namespace word { + namespace emit { + template + struct Local; + } // namespace emit + } // namespace word +} // namespace dsl /** - * @brief The PowerPlant is the core of a NUClear system. It holds all Reactors in it and manages their communications. + * The PowerPlant is the core of a NUClear system. It holds all Reactors in it and manages their communications. * - * @details - * At the centre of every NUClear system is a PowerPlant. A PowerPlant contains all of the reactors that are - * used within the system and sets up their reactions. It is also responsible for storing information between - * reactions and ensuring that all threading is handled appropriately. + * At the centre of every NUClear system is a PowerPlant. + * A PowerPlant contains all of the reactors that are used within the system and sets up their reactions. + * It is also responsible for storing information between reactions and ensuring threading is handled appropriately. */ class PowerPlant { // Reactors and PowerPlants are very tightly linked friend class Reactor; + /** + * This is our Function Fusion wrapper class that allows it to call emit functions + * + * @tparam Handler The emit handler that we are wrapping for + */ + template + struct EmitCaller { + template + static auto call(Arguments&&... args) + // THIS IS VERY IMPORTANT, the return type must be dependent on the function call + // otherwise it won't check it's valid in SFINAE (the comma operator does it again!) + -> decltype(Handler::emit(std::forward(args)...), true) { + Handler::emit(std::forward(args)...); + return true; + } + }; + public: // There can only be one powerplant, so this is it static PowerPlant* powerplant; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) /** - * @brief - * Constructs a PowerPlant with the given configuration and provides access - * to argv for all reactors. + * Constructs a PowerPlant with the given configuration and provides access to argv for all reactors. * - * @details Arguments passed to this function will be emitted as a CommandLineArguments message. + * Arguments passed to this function will be emitted as a CommandLineArguments message. * * @param config The PowerPlant's configuration - * @param argc The number of command line arguments - * @param argv The command line argument strings + * @param argc The number of command line arguments + * @param argv The command line argument strings */ // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) PowerPlant(Configuration config = Configuration(), int argc = 0, const char* argv[] = nullptr); @@ -91,78 +108,85 @@ class PowerPlant { PowerPlant& operator=(const PowerPlant&& other) = delete; /** - * @brief Starts up the PowerPlant's components in order and begins it running. + * Starts up the PowerPlant's components in order and begins it running. * - * @details - * Starts up the PowerPlant instance and starts all the pool threads. This - * method is blocking and will release when the PowerPlant shuts down. - * It should only be called from the main thread so that statics are not - * destructed. + * Starts up the PowerPlant instance and starts all the pool threads. + * This method is blocking and will release when the PowerPlant shuts down. + * It should only be called from the main thread so that statics are not destructed. */ void start(); /** - * @brief Shuts down the PowerPlant, tells all component threads to terminate, - * Then releases the main thread. - */ - void shutdown(); - - /** - * @brief Returns true if the PowerPlant is running or not intending to shut down soon. Returns false otherwise. + * Shuts down the PowerPlant, tells all component threads to terminate and waits for them to finish. + * + * @param force If true, the PowerPlant will shutdown immediately without waiting for tasks to finish */ - bool running() const; + void shutdown(bool force = false); /** - * @brief Installs a reactor of a particular type to the system. + * Installs a reactor of a particular type to the system. * - * @details - * This function constructs a new Reactor of the template type. - * It passes through the specified LogLevel - * in the environment of that reactor so that it can be used to filter logs. + * This function constructs a new Reactor of the template type. + * It passes the specified LogLevel in the environment of that reactor so that it can be used to filter logs. * - * @tparam T The type of the reactor to build and install - * @tparam Args The types of the extra arguments to pass to the reactor constructor - * @tparam level The initial logging level for this reactor to use + * @tparam T The type of the reactor to build and install + * @tparam Args The types of the extra arguments to pass to the reactor constructor + * @tparam level The initial logging level for this reactor to use * - * @param arg Extra arguments to pass to the reactor constructor + * @param arg Extra arguments to pass to the reactor constructor * * @return A reference to the installed reactor */ template - T& install(Args&&... args); + T& install(Args&&... args) { + + // Make sure that the class that we received is a reactor + static_assert(std::is_base_of::value, "You must install Reactors"); + + // The reactor constructor should handle subscribing to events + reactors.push_back(std::make_unique(std::make_unique(*this, util::demangle(typeid(T).name())), + std::forward(args)...)); + + return static_cast(*reactors.back()); + } /** - * @brief Generic submit function for submitting tasks to the thread pool. + * Adds an idle task to the task scheduler. + * + * This function adds an idle task to the task scheduler, which will be executed when the thread pool associated + * with the given `pool_id` has no other tasks to execute. + * The `task` parameter is a Reaction from which a task will be submitted when the pool is idle. * - * @param id an id for ordering the task - * @param priority the priority of the task between 0 and 1000 - * @param group the details of the execution group this task will run in - * @param pool the details of the thread pool this task will run from - * @param immediate if this task should run immediately in the current thread - * @param task the wrapped function to be executed + * @param reaction The reaction to be executed when idle + * @param pool_descriptor The descriptor for the thread pool to test for idle or nullptr for all pools */ - void submit(const NUClear::id_t& id, - const int& priority, - const util::GroupDescriptor& group, - const util::ThreadPoolDescriptor& pool, - const bool& immediate, - std::function&& task); + void add_idle_task(const std::shared_ptr& reaction, + const std::shared_ptr& pool_descriptor = nullptr); /** - * @brief Submits a new task to the ThreadPool to be queued and then executed. + * Removes an idle task from the task scheduler. + * + * This function removes an idle task from the task scheduler. The `id` and `pool_id` parameters are used to + * identify the idle task to be removed. * - * @param task The Reaction task to be executed in the thread pool - * @param immediate if this task should run immediately in the current thread + * @param id The reaction id of the task to be removed + * @param pool_descriptor The descriptor for the thread pool to test for idle */ - void submit(std::unique_ptr&& task, const bool& immediate = false) noexcept; + void remove_idle_task(const NUClear::id_t& id, + const std::shared_ptr& pool_descriptor = nullptr); /** - * @brief Log a message through NUClear's system. + * Submits a new task to the ThreadPool to be queued and then executed. * - * @details - * Logs a message through the system so the various log handlers - * can access it. The arguments being logged should be able to - * be added into a stringstream. + * @param task The Reaction task to be executed in the thread pool + */ + void submit(std::unique_ptr&& task) noexcept; + + /** + * Log a message through NUClear's system. + * + * Logs a message through the system so the various log handlers can access it. + * The arguments being logged should be able to be added into a stringstream. * * @tparam level The level to log at (defaults to DEBUG) * @tparam Arguments The types of the arguments we are logging @@ -170,13 +194,31 @@ class PowerPlant { * @param args The arguments we are logging */ template - static void log(Arguments&&... args); + void log(Arguments&&... args) { + log(level, std::forward(args)...); + } + template + void log(const LogLevel& level, Arguments&&... args) { + std::stringstream ss; + log(level, ss, std::forward(args)...); + } + template + void log(const LogLevel& level, std::stringstream& ss, First&& first, Arguments&&... args) { + ss << std::forward(first) << " "; + log(level, ss, std::forward(args)...); + } + template + void log(const LogLevel& level, std::stringstream& ss, Last&& last) { + ss << std::forward(last); + log(level, ss); + } + void log(const LogLevel& level, std::stringstream& message); + void log(const LogLevel& level, std::string message); /** - * @brief Emits data to the system and routes it to the other systems that use it. + * Emits data to the system and routes it to the other systems that use it. * - * @details - * Emits at Local scope which creates tasks using the thread pool. + * Emits at Local scope which creates tasks using the thread pool. * * @see NUClear::dsl::word::emit::Local for info about Local scope. * @@ -185,28 +227,58 @@ class PowerPlant { * @param data The data we are emitting */ template - void emit(std::unique_ptr&& data); + void emit(std::unique_ptr&& data) { + emit(std::move(data)); + } template - void emit(std::unique_ptr& data); + void emit(std::unique_ptr& data) { + emit(std::move(data)); + } + template