feat(mw::com): Implement Field Notifier support for Generic Skeleton - #455
feat(mw::com): Implement Field Notifier support for Generic Skeleton#455ShoroukRamzy wants to merge 11 commits into
Conversation
1cb68f8 to
e8cdd9f
Compare
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. If this PR is still relevant, please leave a comment or push new changes to keep it open. |
Hi @crimson11, Could you please have a look when you have time? Thanks! |
ae65f9d to
aa82c73
Compare
|
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. If this PR is still relevant, please leave a comment or push new changes to keep it open. |
|
This pull request has been automatically closed due to inactivity. Feel free to reopen it if you would like to continue working on it. |
| GenericSkeletonField& operator=(GenericSkeletonField&& other) & noexcept; | ||
|
|
||
| /// @brief Updates the reference to the parent skeleton when the skeleton is moved. | ||
| void UpdateSkeletonReference(SkeletonBase& skeleton_base) noexcept override; |
There was a problem hiding this comment.
I guess this makes no sense anymore. The code has changed in the meantime and needs rebase.
EnableReferenceToMoveableFromThis has been introduced in the meantime ...
There was a problem hiding this comment.
@crimson11, you are right. I updated the code. Thank you for all your review comments!
| /// @param set_handler The user-provided handler callback accepting raw bytes and returning | ||
| /// the accepted/modified field value as raw bytes. | ||
| /// @return A result indicating success or an error code. | ||
| Result<void> RegisterSetHandler(std::function<std::vector<uint8_t>(score::cpp::span<const uint8_t>)> set_handler); |
There was a problem hiding this comment.
Signature should look different.
The signature in the typed case is: void(FieldType& new_value)
I.e. the provider side callback to the set-handler gets a mutable reference to the value. It can either accept the new value as is ... or it can write to the value to change it, if it needs to correct the new value provided by the client.
There was a problem hiding this comment.
Yes, fully agree with you. done!
| /// @brief Registers a handler for answering Get requests from proxies. | ||
| /// @param get_handler The user-provided handler callback returning the field value as raw bytes. | ||
| /// @return A result indicating success or an error code. | ||
| Result<void> RegisterGetHandler(std::function<std::vector<uint8_t>()> get_handler); |
There was a problem hiding this comment.
In the typed case (normal skeleton), we don't provide the ability to register a "GetHandler" to the user. We shouldn't do it here also. A Get-call should be always handled internally. No need to interact with the user ...
| /// @param set_handler The user-provided handler callback accepting raw bytes and returning | ||
| /// the accepted/modified field value as raw bytes. | ||
| /// @return A result indicating success or an error code. | ||
| Result<void> RegisterSetHandler(std::function<std::vector<uint8_t>(score::cpp::span<const uint8_t>)> set_handler); |
There was a problem hiding this comment.
Maybe state, that an error is returned, when bein called despite has_setter in the ctor was false.
|
|
||
| // Update the parent skeleton's reference to point to this newly moved instance | ||
| SkeletonBaseView skeleton_base_view{skeleton_base_.get()}; | ||
| skeleton_base_view.UpdateField(field_name_, *this); |
There was a problem hiding this comment.
not needed anymore after you have rebased.
|
|
||
| Result<void> GenericSkeletonField::Update(SampleAllocateePtr<void> sample) noexcept | ||
| { | ||
| // If the field is not configured with a notifier, pushing updates is a no-op. |
There was a problem hiding this comment.
This is not true.
Let's assume, we have a field with NO notifier, but WITH Get()! You expect, that the Update being done here is visible to the next Get() call! This you are violating. The updated field data is stored by the binding. The Get() call, which is completely handled internally (see my comment above - there is NO user provided Get-handler), will return the last updated field-value! In our case (LoLa binding) this will be fetched form shared memory! But you have to forward the Update call to the binding! Otherwise this all would not work.
There was a problem hiding this comment.
Thank you @crimson11 for the clarification, Done!
| Result<void> GenericSkeletonField::Update(score::cpp::span<const uint8_t> raw_value) noexcept | ||
| { | ||
| // Cache the initial value if we are updating from a new raw_value payload | ||
| if (raw_value.data() != initial_field_value_.data()) |
There was a problem hiding this comment.
This I do not get. You are doing a pointer comparison here, to find out what?
If you want to find out, whether this is the 1st Update (to set the initial value), why aren't you checking:
initial_field_value_.size ?
|
@ShoroukRamzy Bottom line: The underlying code-base has changed a bit. So you need to rebase. Also the signature of method-handlers got changed. If you rebase and update your changes ... I promise to be more responsive next time ;) |
|
@ShoroukRamzy some checks are failing and there is a comment, please review and adapt |
|
Hi @crimson11, welcome back and thank you for all your review comments. |
aa82c73 to
700ba79
Compare
700ba79 to
11c6443
Compare
No problem! :D - I see there are still some build issues. Ping me when you want a review. |
be23028 to
02a03a6
Compare
|
@ShoroukRamzy just saw that you had formatting-errors ... IDK, if it is obvious, but I always do a
|
02a03a6 to
3df0ea6
Compare
Yes @crimson11, I was verifying an issue on CI that I observed while solving unit test on this PR. I opened a separate PR for it kindly check #825 |
| std::vector<uint8_t> initial_field_value_; | ||
| bool has_initial_value_{false}; | ||
|
|
||
| [[maybe_unused]] bool has_getter_{false}; |
There was a problem hiding this comment.
Are these initializers needed? I guess you have initialization for them in your ctors initializer list! And I did not seen any other ctor, which hasn't.
So - I would expect also that some quality-tools would warn, because this "default" initialization is dead code - will never happen and thus is confusing.
|
|
||
| GenericSkeletonEvent* GenericSkeletonField::GetGenericEvent() const noexcept | ||
| { | ||
| auto* const typed_event = dynamic_cast<GenericSkeletonEvent*>(skeleton_event_dispatch_.get()); |
There was a problem hiding this comment.
typed_event is a funny name here ... where we implement an untyped/type-agnostic generic event ;)
I guess generic_event or generic_skeleton_event would be more suitable.
| namespace score::mw::com::impl | ||
| { | ||
|
|
||
| /// @brief Represents a generic, type-erased skeleton field. |
There was a problem hiding this comment.
not that we have a "golden rule" here ... but I guess we normally use "" instead "@" as doxygen tag prefix ... maybe replace globally in your changed files.
| bool has_getter{false}; | ||
| bool has_setter{false}; | ||
| bool has_notifier{false}; | ||
| score::cpp::span<const uint8_t> initial_value{}; |
There was a problem hiding this comment.
Is this needed here? AFAIS FieldInfo is only "used" accessed in GenericSkeleton ctor .. and there it is not used. So typically the user-code, which created the GenericSkeleton will later expliucitly call Field::Update() before doing the Offer() call ... so - I see no use/need in having this initial_value member here?
| /// with a setter (i.e. has_setter in the constructor was false). | ||
| Result<void> RegisterSetHandler(std::function<void(score::cpp::span<uint8_t>)> set_handler); | ||
|
|
||
| private: |
There was a problem hiding this comment.
No mocking support for GenericSkeletonField? I guess you should add - people need to do unit testing with GenericSkeletonFields exactly like with "typed" SkeletonField ...
| return MakeUnexpected(ComErrc::kBindingFailure); | ||
| } | ||
|
|
||
| auto field_binding_result = |
There was a problem hiding this comment.
it is rather a event_binding_result since on binding level there is no "field" ... on binding level, we have only representations for the composite-members of the field-composite ... when you later create the bindings for getters/setters ... you would also name thzem method_binding_result
| } | ||
|
|
||
| // Helper to fetch the stable field name from the Configuration | ||
| std::string_view GetFieldName(const InstanceIdentifier& identifier, std::string_view search_name) |
There was a problem hiding this comment.
I guess you need to extend generic_skeleton_test.cpp for testing this?
| } | ||
| } | ||
|
|
||
| // 3. Create fields directly in the map |
There was a problem hiding this comment.
I guess you need to extend generic_skeleton_test.cpp for testing this extended Create() functionality?
| @@ -0,0 +1,354 @@ | |||
| /******************************************************************************** | |||
There was a problem hiding this comment.
Looking at the tests here, they seem to be structurally a bit different than the tests in generic_skeleton_event_test.cpp
Why? Doesn't it make sense to "duplicate" structure/approach?
And then please also use always in tests the given-when-expect pattern to document the test as we (hopefully) do in all tests.
1f88edb to
b632800
Compare
Summary:
This PR extends the GenericSkeleton to support type-erased Fields and solves this issue #181
Key Changes:
GenericSkeletonField: Introduced as a type-erased facade for fields. It takes ownership of a GenericSkeletonEvent under the hood to handle shared memory interactions and broadcasts.
Initial Value Caching: Calling Update() before OfferService() caches the field payload locally. Once the service is offered, DoDeferredUpdate() automatically allocates memory and dispatches this initial value to subscribers.
Zero-copy Notifications: Added support for Allocate() and Update(SampleAllocateePtr) to push new field values to subscribers after the service is actively running.
Skeleton Orchestration: Updated GenericSkeleton::Create to parse field configurations, spawn the fields, and properly populate the internal fields_ map.
Next Steps / Future Work:
Generic Getters & Setters: Currently, RegisterGetHandler and RegisterSetHandler are stubbed out as WIP and return ComErrc::kCouldNotExecute. In the next step, this generic field will be extended with full set and get method support over shared memory once those features are fully implemented.