Graph refactor initial components - #370
Conversation
License Check Results🚀 The license check job ran with the Bazel command: bazel run --lockfile_mode=error //:license-checkStatus: Click to expand output |
fe8eca0 to
04b715d
Compare
04b715d to
5001193
Compare
5001193 to
9c02cd9
Compare
|
The created documentation from the pull request is available at: docu-html |
MaciejKaszynski
left a comment
There was a problem hiding this comment.
Looks good, would really like to split this into separate folders because it's a bit unclear if we are at the component level or process level.
| #include <cstdint> | ||
|
|
||
| namespace score { | ||
| namespace score |
There was a problem hiding this comment.
I think the namespace should be score::mw::lifecycle so here score::mw::lifecycle::internal
There was a problem hiding this comment.
This whole PR could update this.
There was a problem hiding this comment.
Should we use this namespace for the new code and then update the other namespaces with a separate PR?
There was a problem hiding this comment.
I would say let's just fix what is touched with this PR
| { | ||
|
|
||
| namespace lcm { | ||
| namespace lcm |
There was a problem hiding this comment.
We should also enable the concat-nested-namespaces clang-tidy rule to concatenate the nested namepsaces
| 256U, ///< Maximum number of ControlClient instances that should be created by state manager. If state manager create more instances than kMaxInstances, those instances will always return kCommunicationError when used | ||
| enum class ControlClientLimits : uint16_t | ||
| { | ||
| kControlClientMaxInstances = 256U, ///< Maximum number of ControlClient instances that should be created by state |
There was a problem hiding this comment.
I don't think you actually touched this line but could you convert this to just a variable and have the enum become a namespace. I think then this all would be less weird.
There was a problem hiding this comment.
I've reverted the changes in this file now, it wouldn't make sense to change this in this PR
| TEST_F(ComponentEventQueueTest, WaitForEventsReturnsFalseOnEmptyQueue) | ||
| { | ||
| RecordProperty("Description", "Verify waitForEvents returns false promptly when no event has been pushed."); | ||
| EXPECT_FALSE(queue_.waitForEvents(std::chrono::milliseconds{1})); |
There was a problem hiding this comment.
| EXPECT_FALSE(queue_.waitForEvents(std::chrono::milliseconds{1})); | |
| EXPECT_FALSE(queue_.waitForEvents(std::chrono::milliseconds{0})); |
The queue should handle 0 here.
| namespace score::lcm::internal | ||
| { | ||
|
|
||
| class IComponentEventReceiver |
|
|
||
| /// @brief Start work on @p task and push the result to the event queue if the task completes | ||
| void doWork(Task task) override; | ||
| /// @brief Notify @p component that it has terminated with status @p status. If this is an error or finishes a |
There was a problem hiding this comment.
Can you space stuff out so it's more readable.
| /// @brief Notify @p component that it has terminated with status @p status. If this is an error or finishes a | |
| /// @brief Notify @p component that it has terminated with status @p status. If this is an error or finishes a |
| void terminated(IComponent& component, int32_t status) override; | ||
|
|
||
| private: | ||
| void taskFinished(const Task& task, const score::cpp::expected_blank<IComponent::ComponentError>& error); |
There was a problem hiding this comment.
still need briefs on private :) also feel like the name should be markFinished.
There was a problem hiding this comment.
Actually you might want to just make this a lambda in doWork
| using namespace testing; | ||
| using namespace score::lcm::internal; | ||
|
|
||
| class MockComponent : public IComponent |
There was a problem hiding this comment.
Can you make this into a separate header for easier re-use.
| class IComponentController | ||
| { | ||
| public: | ||
| virtual void doWork(Task task) = 0; |
There was a problem hiding this comment.
Are you sure about copying, seems like we could just always take a && here?
|
|
||
| /// @brief Queue of ComponentEvents produced by worker/OS-handler threads and consumed | ||
| /// exclusively by the main thread, backed by a fixed-capacity MpscBoundedQueue. | ||
| class ComponentEventQueue final : public IComponentEventReceiver |
There was a problem hiding this comment.
Should we rename this interface to e.g. IComponentEventPublisher?
It looks like its for publishing events into the queue, not actually for reading events from the queue
There was a problem hiding this comment.
Yes, the name seems to be incorrect. Should we have two interfaces IComponentEventPublisher and IComponentEventSubscriber? And the queue would implement both
| }; | ||
|
|
||
| /// @brief Capacity of the ComponentEventQueue. | ||
| constexpr std::size_t kComponentEventQueueSize = 1024U; |
There was a problem hiding this comment.
Maybe copy the way it is done in MpscBoundedQueue, and make the ComponentEventQueue take the capacity as a type parameter:
This would allow mocking the capacity in tests, having multiple queues with different capacities, etc
| default: | ||
| break; |
There was a problem hiding this comment.
Should we add SCORE_LANGUAGE_FUTURECPP_UNREACHABLE here? If someone defines a new task type and does not add code to handle it the behaviour is not clear
| if (res.has_value() && res.value() == IComponent::RequestState::kWaiting) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (res.has_value()) | ||
| { | ||
| taskFinished(task, {}); | ||
| } | ||
| else | ||
| { | ||
| taskFinished(task, score::cpp::make_unexpected(res.error())); | ||
| } |
There was a problem hiding this comment.
Personally I would find this easier to read if it were nested
| if (res.has_value() && res.value() == IComponent::RequestState::kWaiting) | |
| { | |
| return; | |
| } | |
| if (res.has_value()) | |
| { | |
| taskFinished(task, {}); | |
| } | |
| else | |
| { | |
| taskFinished(task, score::cpp::make_unexpected(res.error())); | |
| } | |
| if (res.has_value()) | |
| { | |
| if (res.value() == IComponent::RequestState::kWaiting) | |
| { | |
| return; | |
| } | |
| taskFinished(task, {}); | |
| } | |
| else | |
| { | |
| taskFinished(task, score::cpp::make_unexpected(res.error())); | |
| } |
| else if (error.has_value()) | ||
| { | ||
| event_queue_.push(ActivationSuccessful{node_index}); | ||
| } | ||
| else | ||
| { | ||
| event_queue_.push(ActivationFailed{node_index, error.error()}); | ||
| } |
There was a problem hiding this comment.
This is correct but because of the naming it reads the wrong way round- "if there is an error, push successful"...
Maybe rename the parameter to something like status or result?
| @@ -1,2 +1,2 @@ | |||
| Checks: ' -*, bugprone-*, cert-*, clang-analyzer-core.*, clang-analyzer-cplusplus.*, clang-analyzer-security.*, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-cstyle-cast, hicpp-* ' | |||
| WarningsAsErrors: 'bugprone-*,cert-*,clang-analyzer-*,hicpp-*' | |||
| WarningsAsErrors: 'bugprone-*,cert-*,clang-analyzer-*' | |||
There was a problem hiding this comment.
I made all of my interfaces show up as errors 😄 . I've refined it now in acaf3c9, I added an option that allows classes with default destructors only
| RecordProperty("Description", | ||
| "Verify stop() causes a subsequently-called waitForEvents() to return false immediately " | ||
| "rather than blocking, matching the shutdown usage in ProcessGroupManager::deinitialize()."); | ||
| queue_.stop(); | ||
| EXPECT_FALSE(queue_.waitForEvents(std::chrono::milliseconds{2000})); |
There was a problem hiding this comment.
This test would still pass if it blocks. Or we are relying on the timeout of the test framework to make it fail
There was a problem hiding this comment.
I don't think there's any way to test this without fragility, I've changed the test slightly: ebc75c3
| { | ||
|
|
||
| class ComponentEventQueueTest : public ::testing::Test | ||
| { |
There was a problem hiding this comment.
I think we need to add here
RecordProperty("TestType", "interface-test");
RecordProperty("DerivationTechnique", "explorative-testing");
This resolves several review comments: - `taskFinished` is no longer present in the header - Confusing nesting is improved by having a clear switch statement for each purpose - By removing the default case we get a compiler warning if a possible value is not handled
ccdddfe to
bcbda43
Compare
128f839 to
2052521
Compare
This PR introduces some initial components required for an upcoming refactor to the
GraphandProcessInfoNodeclasses. Firstly, we want to introduce some components that are more easily understood and unit tested. The next PR will integrate these into existing code. Excluding interfaces and data-only classes, the new components are:ComponentEventQueue- Allows us to handle component state changes sequentially in GraphProcessMonitor- DecouplesProcessInfoNodefromGraphand allows us to simplify its APIsThe refactor aims to address the following concerns:
Graph