Muse skeleton get remaining part#48
Muse skeleton get remaining part#48saksham-chawla wants to merge 6 commits intocodex/replay-20260319023913-pr-207-basefrom
Conversation
- Adds GetLatestSlot function to EventDataControlComposite Issue: SWP-249517
- Adds GetLatestSampleFunction to SkeletonEvent Issue: SWP-249517
Implement RegisterSetHandler() method to allow users to register callbacks for handling remote Set requests on skeleton fields. The Set handler wraps user-provided validation/modification logic and automatically broadcasts accepted values to all field subscribers via Update(). Configuration support is stubbed with TODO - requires extension of LolaFieldInstanceDeployment to include has_setter flag. Issue: SWP-249527
Replace runtime is_setter_ guard with EnableSet/EnableNotifier template bool parameters on SkeletonField. Issue: SWP-249527
- SkeletonField now includes a new template parameter, EnableGet. - When EnableGet is true, a skeleton Get method attribute is added to SkeletonField. - Constructors were updated to support all relevant EnableSet and EnableGet combinations. Issue: SWP-249517
- Automatically register a Get handler for fields with EnableGet. - Serve Get requests by reading the latest field value and returning it through the Get method. - Update the required slot calculation. - Add a test covering this behavior. Issue: SWP-249517
There was a problem hiding this comment.
Review found a critical move-related handler bug plus several logic/reliability issues needing fixes.
Status: Action Required | Risk: High | Readiness: 3/10 (Needs Significant Work)
Issues Identified & Suggestions
- Move/handler capture causes UAF; re-register after move:
score/mw/com/impl/skeleton_field.h - Testing ctor SFINAE mismatch blocks EnableSet/Get tests:
score/mw/com/impl/skeleton_field.h - RegisterGetHandler failure only logged; propagate or terminate:
score/mw/com/impl/skeleton_field.h - Ignore QM disconnect in GetLatestSlot to return ASIL-B:
score/mw/com/impl/bindings/lola/event_data_control_composite.cpp
Review Details
📁 15 files reviewed | 💬 4 comments
👍 / 👎 individual comments to help improve reviews for you
| template <typename SampleDataType, bool EnableSet, bool EnableNotifier, bool EnableGet> | ||
| SkeletonField<SampleDataType, EnableSet, EnableNotifier, EnableGet>::SkeletonField(SkeletonField&& other) noexcept | ||
| : SkeletonFieldBase(static_cast<SkeletonFieldBase&&>(other)), | ||
| // known llvm bug (https://github.com/llvm/llvm-project/issues/63202) | ||
| // This usage is safe because the previous line only moves the base class portion via static_cast. | ||
| // The derived class member 'initial_field_value_' remains untouched by the base class move constructor, | ||
| // so it's still valid to access it here for moving into our own member. | ||
| // coverity[autosar_cpp14_a12_8_3_violation] This is a false-positive. | ||
| initial_field_value_{std::move(other.initial_field_value_)}, | ||
| skeleton_field_mock_{other.skeleton_field_mock_} | ||
| skeleton_field_mock_{other.skeleton_field_mock_}, | ||
| set_method_{std::move(other.set_method_)}, |
There was a problem hiding this comment.
[Reliability] After move-constructing or move-assigning a SkeletonField with EnableGet=true, the lambda registered via RegisterGetHandler() still captures the this pointer of the moved-from object. When a Get request is subsequently processed on the moved-to object, the handler (now owned by the moved-to get_method_) dereferences the stale this pointer, accessing get_handler_mutex_ and calling GetTypedEvent() on the moved-from (potentially destroyed) object. This is a use-after-free / dangling pointer bug.
The same issue applies to RegisterSetHandler — the wrapped_callback lambda captures this, so after move, this->Update(result.value()) targets the wrong object.
Trigger: Move-construct or move-assign a SkeletonField<T, ES, EN, true> (or one where RegisterSetHandler was called), then invoke the get/set method on the new object.
Impact: Undefined behavior — reads/writes to freed memory, potential crash or data corruption.
The move constructor and move assignment operator need to re-register the get handler (and potentially the set handler) so the lambda captures the correct this pointer of the new object.
Context for Agents
[**Bug**] After move-constructing or move-assigning a `SkeletonField` with `EnableGet=true`, the lambda registered via `RegisterGetHandler()` still captures the `this` pointer of the **moved-from** object. When a Get request is subsequently processed on the moved-to object, the handler (now owned by the moved-to `get_method_`) dereferences the stale `this` pointer, accessing `get_handler_mutex_` and calling `GetTypedEvent()` on the moved-from (potentially destroyed) object. This is a use-after-free / dangling pointer bug.
The same issue applies to `RegisterSetHandler` — the `wrapped_callback` lambda captures `this`, so after move, `this->Update(result.value())` targets the wrong object.
**Trigger:** Move-construct or move-assign a `SkeletonField<T, ES, EN, true>` (or one where `RegisterSetHandler` was called), then invoke the get/set method on the new object.
**Impact:** Undefined behavior — reads/writes to freed memory, potential crash or data corruption.
The move constructor and move assignment operator need to re-register the get handler (and potentially the set handler) so the lambda captures the correct `this` pointer of the new object.
File: score/mw/com/impl/skeleton_field.h
Line: 434Chain of Thought
- Validation Decision: accepted
- Confidence: 0.95
- Likelihood Score: 4/5
- Likelihood Reason: If anyone move-constructs or move-assigns a SkeletonField with EnableGet=true (or after calling RegisterSetHandler), and then processes a get/set request on the moved-to object, the handler will dereference a stale
thispointer. The move constructor is explicitly defined and used (UpdateField is called), making moves a supported operation. The bug will manifest whenever move semantics are actually exercised on an instance with registered handlers. - Suggested Action: keep
- Tool Calls Used: 5
Reasoning
The comment correctly identifies a use-after-free / dangling-pointer bug introduced by this PR. Here's the evidence:
-
RegisterGetHandler() (line 201) creates a lambda
[this]() -> Result<FieldType> { std::lock_guard<std::mutex> lock{get_handler_mutex_}; return SkeletonEventView<FieldType>{*GetTypedEvent()}.GetLatestSample(); }which capturesthis(the SkeletonField pointer) at registration time. This handler is stored insideget_method_'s binding. -
Move constructor (lines 424-440) moves
get_method_viastd::move(other.get_method_). Sinceget_method_is aunique_ptr<SkeletonMethod<...>>, this just transfers pointer ownership — the underlying SkeletonMethod object on the heap is unchanged, and its stored handler lambda still captures the oldthis(pointing to the moved-from SkeletonField). There is NO call toRegisterGetHandler()after the move. -
Move assignment (lines 442-460) has the same issue —
get_method_is move-assigned but the handler isn't re-registered. -
RegisterSetHandler (line 158) similarly captures
thisin a lambda (this->Update(result.value())), which will also dangle after move. -
SkeletonMethod::RegisterHandler (skeleton_method.h line 126-171) stores the callback permanently inside the binding. There's no mechanism to rebind the captured
thispointer.
This is entirely new code introduced by this PR. All of get_method_, set_method_, RegisterGetHandler(), and RegisterSetHandler() are newly added. The fix would be to re-register handlers (with the new this) in the move constructor and move assignment operator.
The comment is accurate, specific, and actionable — it identifies the exact bug, the trigger condition, and the needed fix.
Evidence Summary
1. File Lookup
- Filename:
skeleton_method.h
Found 3 file(s) matching 'skeleton_method.h':
- score/mw/com/impl/bindings/lola/skeleton_method.h
- score/mw/com/impl/bindings/mock_binding/skeleton_method.h
- score/mw/com/impl/methods/skeleton_method.h
2. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
424-460
424: template <typename SampleDataType, bool EnableSet, bool EnableNotifier, bool EnableGet>
425: SkeletonField<SampleDataType, EnableSet, EnableNotifier, EnableGet>::SkeletonField(SkeletonField&& other) noexcept
426: : SkeletonFieldBase(static_cast<SkeletonFieldBase&&>(other)),
427: // known llvm bug (https://github.com/llvm/llvm-project/issues/63202)
428: // This usage is safe because the previous line only moves the base class portion via static_cast.
429: // The derived class member 'initial_field_value_' remains untouched by the base class move constructor,
430: // so it's still valid to access it here for moving into our own member.
431: // coverity[autosar_cpp14_a12_8_3_violation] This is a false-positive.
432: initial_field_value_{std::move(other.initial_field_value_)},
433: skeleton_field_mock_{other.skeleton_field_mock_},
...
3. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
195-215
195: // Get handler registration
196: // SFINAE-gated: only exists when EnableGet == true
197: template <bool EG = EnableGet, typename std::enable_if<EG, int>::type = 0>
198: void RegisterGetHandler()
199: {
200: // get_method_ is guaranteed to be unique_ptr here since RegisterGetHandler is SFINAE-gated on EnableGet=true.
201: const auto result = get_method_.get()->RegisterHandler([this]() -> Result<FieldType> {
202: // need to serialize access to Get. In case of concurrent Get calls,
203: // we want to ensure that they are processed sequentially.
204: std::lock_guard<std::mutex> lock{get_handler_mutex_};
...
4. File Snapshot (HEAD)
- File:
score/mw/com/impl/methods/skeleton_method.h
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
5. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
152-180
152: // Set handler registration
153: // SFINAE-gated: only exists when EnableSet == true
154: template <typename Callable, bool ES = EnableSet, typename std::enable_if<ES, int>::type = 0>
155: ResultBlank RegisterSetHandler(Callable&& callback)
156: {
157: // Wrap user callback to also call Update() after user validation
158: auto wrapped_callback = [this, user_callback = std::move(callback)](FieldType new_value) -> Result<FieldType> {
159: // 1. Call user's validation/modification handler
160: auto result = user_callback(new_value);
161:
...
| EventDataControl* control = (asil_b_control_ != nullptr) ? asil_b_control_ : asil_qm_control_; | ||
|
|
||
| for (std::size_t retry_counter{0U}; retry_counter < MAX_MULTI_ALLOCATE_RETRY_COUNT; ++retry_counter) | ||
| { | ||
| const auto latest_slot_index = FindLatestReadableSlotIndex(*control); | ||
|
|
||
| if (!latest_slot_index.has_value()) | ||
| { | ||
| return {}; | ||
| } | ||
|
|
There was a problem hiding this comment.
[Logic] When ignore_qm_control_ is set (QM consumers misbehaved and we stopped updating the QM control), GetLatestSlot() still tries to grab a QM slot and only returns a slot if the QM reference count can be incremented. After the disconnect, QM slots stay invalid forever, so TryIncreaseReferenceCount(slot_qm) always fails and the method returns {} even though the ASIL‑B control still contains the latest sample. This means SkeletonEvent::GetLatestSample() will never succeed once QM has been disconnected.
We need to skip the QM branch when ignore_qm_control_ is true and just operate on the ASIL‑B slot, similar to the existing QM‑only code path:
@@
- if (asil_b_control_ != nullptr)
- {
- auto& slot_qm = asil_qm_control_->state_slots_[slot_index];
- auto& slot_asil_b = asil_b_control_->state_slots_[slot_index];
-
- if (!TryIncreaseReferenceCount(slot_qm))
- {
- continue;
- }
-
- if (!TryIncreaseReferenceCount(slot_asil_b))
- {
- score::cpp::ignore = slot_qm.fetch_sub(1U, std::memory_order_acq_rel);
- continue;
- }
-
- return {slot_index, slot_qm, slot_asil_b};
- }
+ if (asil_b_control_ != nullptr)
+ {
+ auto& slot_asil_b = asil_b_control_->state_slots_[slot_index];
+
+ if (!ignore_qm_control_)
+ {
+ auto& slot_qm = asil_qm_control_->state_slots_[slot_index];
+ if (!TryIncreaseReferenceCount(slot_qm))
+ {
+ continue;
+ }
+
+ if (!TryIncreaseReferenceCount(slot_asil_b))
+ {
+ score::cpp::ignore = slot_qm.fetch_sub(1U, std::memory_order_acq_rel);
+ continue;
+ }
+
+ return {slot_index, slot_qm, slot_asil_b};
+ }
+
+ if (!TryIncreaseReferenceCount(slot_asil_b))
+ {
+ continue;
+ }
+
+ return {slot_index, slot_asil_b, ControlSlotCompositeIndicator::CompositeSlotTagType::ASIL_B};
+ }This keeps ASIL‑B GETs working after a QM disconnect.
Context for Agents
When `ignore_qm_control_` is set (QM consumers misbehaved and we stopped updating the QM control), `GetLatestSlot()` still tries to grab a QM slot and only returns a slot if the QM reference count can be incremented. After the disconnect, QM slots stay invalid forever, so `TryIncreaseReferenceCount(slot_qm)` always fails and the method returns `{}` even though the ASIL‑B control still contains the latest sample. This means `SkeletonEvent::GetLatestSample()` will never succeed once QM has been disconnected.
We need to skip the QM branch when `ignore_qm_control_` is true and just operate on the ASIL‑B slot, similar to the existing QM‑only code path:
```diff
@@
- if (asil_b_control_ != nullptr)
- {
- auto& slot_qm = asil_qm_control_->state_slots_[slot_index];
- auto& slot_asil_b = asil_b_control_->state_slots_[slot_index];
-
- if (!TryIncreaseReferenceCount(slot_qm))
- {
- continue;
- }
-
- if (!TryIncreaseReferenceCount(slot_asil_b))
- {
- score::cpp::ignore = slot_qm.fetch_sub(1U, std::memory_order_acq_rel);
- continue;
- }
-
- return {slot_index, slot_qm, slot_asil_b};
- }
+ if (asil_b_control_ != nullptr)
+ {
+ auto& slot_asil_b = asil_b_control_->state_slots_[slot_index];
+
+ if (!ignore_qm_control_)
+ {
+ auto& slot_qm = asil_qm_control_->state_slots_[slot_index];
+ if (!TryIncreaseReferenceCount(slot_qm))
+ {
+ continue;
+ }
+
+ if (!TryIncreaseReferenceCount(slot_asil_b))
+ {
+ score::cpp::ignore = slot_qm.fetch_sub(1U, std::memory_order_acq_rel);
+ continue;
+ }
+
+ return {slot_index, slot_qm, slot_asil_b};
+ }
+
+ if (!TryIncreaseReferenceCount(slot_asil_b))
+ {
+ continue;
+ }
+
+ return {slot_index, slot_asil_b, ControlSlotCompositeIndicator::CompositeSlotTagType::ASIL_B};
+ }
```
This keeps ASIL‑B GETs working after a QM disconnect.
File: score/mw/com/impl/bindings/lola/event_data_control_composite.cpp
Line: 367Chain of Thought
- Validation Decision: accepted
- Confidence: 0.00
- Likelihood Score: 0/5
- Suggested Action: keep
- Tool Calls Used: 9
Reasoning
Failed to parse validation response
Evidence Summary
1. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/event_data_control_composite.h
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
2. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/event_data_control_composite.cpp - Lines:
354-399
354: template <template <class> class AtomicIndirectorType>
355: ControlSlotCompositeIndicator EventDataControlCompositeImpl<AtomicIndirectorType>::GetLatestSlot() const noexcept
356: {
357: EventDataControl* control = (asil_b_control_ != nullptr) ? asil_b_control_ : asil_qm_control_;
358:
359: for (std::size_t retry_counter{0U}; retry_counter < MAX_MULTI_ALLOCATE_RETRY_COUNT; ++retry_counter)
360: {
361: const auto latest_slot_index = FindLatestReadableSlotIndex(*control);
362:
363: if (!latest_slot_index.has_value())
...
3. File Snapshot (BASE)
- File:
score/mw/com/impl/bindings/lola/event_data_control_composite.cpp - Lines:
1-100
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
4. File Lookup
- Filename:
control_slot_composite_indicator.h
Found 1 file(s) matching 'control_slot_composite_indicator.h':
- score/mw/com/impl/bindings/lola/control_slot_composite_indicator.h
5. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/event_data_control_composite.cpp - Lines:
89-350
89: template <template <class> class AtomicIndirectorType>
90: // Suppress "AUTOSAR C++14 A15-5-3" rule findings. This rule states: "The std::terminate() function shall not be called
91: // implicitly". This is a false positive, all results which are accessed with '.value()' that could implicitly call
92: // 'std::terminate()' (in case it doesn't have value) has a check in advance using '.has_value()', so no way for
93: // throwing std::bad_optional_access which leds to std::terminate(). This suppression should be removed after fixing
94: // [Ticket-173043](broken_link_j/Ticket-173043)
95: // coverity[autosar_cpp14_a15_5_3_violation : FALSE]
96: auto EventDataControlCompositeImpl<AtomicIndirectorType>::GetNextFreeMultiSlot() const noexcept
97: -> ControlSlotCompositeIndicator
98: {
...
6. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/control_slot_composite_indicator.h
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
7. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/skeleton_event.h - Lines:
1-150
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
8. File Snapshot (HEAD)
- File:
score/mw/com/impl/bindings/lola/skeleton_event.h - Lines:
150-398
150: impl::tracing::SkeletonEventTracingData skeleton_event_tracing_data) noexcept
151: : SkeletonEventBinding<SampleType>{},
152: parent_{parent},
153: event_fqn_{event_fqn},
154: event_name_{event_name},
155: event_properties_{properties},
156: event_data_storage_{nullptr},
157: event_data_control_composite_{std::nullopt},
158: current_timestamp_{1U},
159: qm_disconnect_{false},
...
| SkeletonField<SampleDataType, EnableSet, EnableNotifier, EnableGet>::SkeletonField( | ||
| SkeletonBase& skeleton_base, | ||
| const std::string_view field_name, | ||
| std::unique_ptr<SkeletonEventBinding<FieldType>> binding) | ||
| : SkeletonField{skeleton_base, | ||
| std::make_unique<SkeletonEvent<FieldType>>(skeleton_base, field_name, std::move(binding)), | ||
| field_name} | ||
| { | ||
| } |
There was a problem hiding this comment.
[Logic] The testing constructor (3-arg ctor taking SkeletonBase&, string_view, unique_ptr<SkeletonEventBinding>) always delegates to a private constructor with signature (SkeletonBase&, unique_ptr<SkeletonEvent>, string_view). Only the !ES && !EG private delegating constructor matches this signature. When EnableSet=true or EnableGet=true, the SFINAE condition std::enable_if_t<!ES && !EG> on that constructor eliminates it, and no other private constructor matches (they require additional set_method/get_method arguments). This results in a compilation error when attempting to instantiate the testing constructor for any SkeletonField with EnableSet=true or EnableGet=true.
This means it is impossible to write tests using mock bindings for any SkeletonField that has Get or Set enabled via this constructor.
Context for Agents
[**Bug**] The testing constructor (3-arg ctor taking `SkeletonBase&`, `string_view`, `unique_ptr<SkeletonEventBinding>`) always delegates to a private constructor with signature `(SkeletonBase&, unique_ptr<SkeletonEvent>, string_view)`. Only the `!ES && !EG` private delegating constructor matches this signature. When `EnableSet=true` or `EnableGet=true`, the SFINAE condition `std::enable_if_t<!ES && !EG>` on that constructor eliminates it, and no other private constructor matches (they require additional `set_method`/`get_method` arguments). This results in a **compilation error** when attempting to instantiate the testing constructor for any `SkeletonField` with `EnableSet=true` or `EnableGet=true`.
This means it is impossible to write tests using mock bindings for any `SkeletonField` that has Get or Set enabled via this constructor.
File: score/mw/com/impl/skeleton_field.h
Line: 352Chain of Thought
- Validation Decision: accepted
- Confidence: 0.00
- Likelihood Score: 0/5
- Suggested Action: keep
- Tool Calls Used: 10
Reasoning
Failed to parse validation response
Evidence Summary
1. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
342-355
342: /// \brief Testing ctor: binding is provided directly (used with mock bindings in tests).
343: template <typename SampleDataType, bool EnableSet, bool EnableNotifier, bool EnableGet>
344: SkeletonField<SampleDataType, EnableSet, EnableNotifier, EnableGet>::SkeletonField(
345: SkeletonBase& skeleton_base,
346: const std::string_view field_name,
347: std::unique_ptr<SkeletonEventBinding<FieldType>> binding)
348: : SkeletonField{skeleton_base,
349: std::make_unique<SkeletonEvent<FieldType>>(skeleton_base, field_name, std::move(binding)),
350: field_name}
351: {
...
2. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
99-145
99:
100: /// Constructor that allows to set the binding directly.
101: ///
102: /// This is used only used for testing.
103: SkeletonField(SkeletonBase& skeleton_base,
104: const std::string_view field_name,
105: std::unique_ptr<SkeletonEventBinding<FieldType>> binding);
106:
107: ~SkeletonField() override = default;
108:
...
3. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
233-260
233: /// \brief Private delegating constructor: EnableSet=true, EnableGet=true.
234: template <bool ES = EnableSet, bool EG = EnableGet, typename = std::enable_if_t<ES && EG>>
235: SkeletonField(SkeletonBase& parent,
236: std::unique_ptr<SkeletonEvent<FieldType>> skeleton_event_dispatch,
237: std::unique_ptr<SkeletonMethod<Result<FieldType>(FieldType)>> set_method,
238: std::unique_ptr<SkeletonMethod<Result<FieldType>()>> get_method,
239: const std::string_view field_name);
240:
241: /// \brief Private delegating constructor: EnableSet=false, EnableGet=true.
242: template <bool ES = EnableSet, bool EG = EnableGet, typename = std::enable_if_t<!ES && EG>>
...
4. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Lines:
1-80
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
5. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Lines:
80-200
80: RecordProperty("Description", "Checks copy constructors for SkeletonField");
81: RecordProperty("TestType", "Requirements-based test");
82: RecordProperty("Priority", "1");
83: RecordProperty("DerivationTechnique", "Analysis of requirements");
84:
85: static_assert(!std::is_copy_constructible<SkeletonField<TestSampleType>>::value, "Is wrongly copyable");
86: static_assert(!std::is_copy_assignable<SkeletonField<TestSampleType>>::value, "Is wrongly copyable");
87: }
88:
89: TEST(SkeletonFieldTest, IsMoveable)
...
6. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Lines:
300-500
300: // then it does not return an error
301: ASSERT_TRUE(update_result.has_value());
302:
303: // and send is not called on the binding
304: EXPECT_FALSE(is_send_called_on_binding);
305:
306: // and when PrepareOffer() is called on the field
307: const auto prepare_offer_result = unit.my_dummy_field_.PrepareOffer();
308:
309: // Then the result will contain an error that the binding failed
...
7. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Lines:
200-300
200:
201: // When Ticket-104261 is implemented, the Update call does not have to be deferred until OfferService is called. This
202: // test can be reworked to remove the call to PrepareOffer() and simply test Update() before PrepareOffer() is called.
203: TEST(SkeletonFieldCopyUpdateTest, CallingUpdateBeforeOfferServiceDefersCallToOfferService)
204: {
205: RecordProperty("Verifies", "SCR-17434775, SCR-17563743, SCR-21553554");
206: RecordProperty("Description", "Checks that calling Update before offer service defers the call to OfferService().");
207: RecordProperty("TestType", "Requirements-based test");
208: RecordProperty("Priority", "1");
209: RecordProperty("DerivationTechnique", "Analysis of requirements");
...
8. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
100-260
100: /// Constructor that allows to set the binding directly.
101: ///
102: /// This is used only used for testing.
103: SkeletonField(SkeletonBase& skeleton_base,
104: const std::string_view field_name,
105: std::unique_ptr<SkeletonEventBinding<FieldType>> binding);
106:
107: ~SkeletonField() override = default;
108:
109: SkeletonField(const SkeletonField&) = delete;
...
| if (!result.has_value()) | ||
| { | ||
| score::mw::log::LogError("lola") | ||
| << "RegisterGetHandler: failed to register get handler: " << result.error(); | ||
| } |
There was a problem hiding this comment.
[Reliability] RegisterGetHandler failure is silently logged but not propagated, and construction continues. If handler registration fails, the SkeletonField is constructed in a broken state where Get requests will never be served. The error should be made more visible — consider calling std::terminate(), storing the failure for later checking, or propagating the error from the constructor rather than only logging.
| if (!result.has_value()) | |
| { | |
| score::mw::log::LogError("lola") | |
| << "RegisterGetHandler: failed to register get handler: " << result.error(); | |
| } | |
| if (!result.has_value()) | |
| { | |
| score::mw::log::LogFatal("lola") | |
| << "RegisterGetHandler: failed to register get handler: " << result.error(); | |
| std::terminate(); | |
| } |
Context for Agents
`RegisterGetHandler` failure is silently logged but not propagated, and construction continues. If handler registration fails, the `SkeletonField` is constructed in a broken state where Get requests will never be served. The error should be made more visible — consider calling `std::terminate()`, storing the failure for later checking, or propagating the error from the constructor rather than only logging.
```suggestion
if (!result.has_value())
{
score::mw::log::LogFatal("lola")
<< "RegisterGetHandler: failed to register get handler: " << result.error();
std::terminate();
}
```
File: score/mw/com/impl/skeleton_field.h
Line: 211Chain of Thought
- Validation Decision: accepted
- Confidence: 0.82
- Likelihood Score: 3/5
- Likelihood Reason: The RegisterHandler binding call returns ResultBlank, explicitly allowing failure. While it may rarely fail in practice (e.g., double registration), the code silently proceeds leaving the SkeletonField in a state where Get requests are never served, which could cause hard-to-diagnose issues in production.
- Suggested Action: modify
- Tool Calls Used: 15
Reasoning
The comment identifies a genuine issue newly introduced by this PR:
-
New code: The
RegisterGetHandler()method (lines 195-212) and its invocation from constructors (lines 371, 389) are entirely new in this PR. -
Silent failure: When
get_method_->RegisterHandler(...)fails, the code only logs viaLogErrorand continues. The method returnsvoid, so the error is not propagated to the caller. -
Broken state: If handler registration fails, the
SkeletonFieldis constructed without a get handler. This means proxy Get requests will never be served, but the object appears valid. This is a genuine design concern. -
Actionable suggestion: The comment provides a concrete code replacement using
LogFatal+std::terminate(). -
Minor inaccuracy: The comment mentions "noexcept-adjacent (move ctor is noexcept)" which is slightly misleading - the regular constructors that call
RegisterGetHandlerare NOT noexcept, so they could technically throw. However, the core point about the error being silently swallowed remains valid regardless.
The std::terminate() suggestion is opinionated (another valid approach might be to propagate the error), but the core issue - that a critical handler registration failure is silently logged and ignored, leaving the object in a non-functional state - is accurate, actionable, and introduced by this PR.
Evidence Summary
1. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
195-215
195: // Get handler registration
196: // SFINAE-gated: only exists when EnableGet == true
197: template <bool EG = EnableGet, typename std::enable_if<EG, int>::type = 0>
198: void RegisterGetHandler()
199: {
200: // get_method_ is guaranteed to be unique_ptr here since RegisterGetHandler is SFINAE-gated on EnableGet=true.
201: const auto result = get_method_.get()->RegisterHandler([this]() -> Result<FieldType> {
202: // need to serialize access to Get. In case of concurrent Get calls,
203: // we want to ensure that they are processed sequentially.
204: std::lock_guard<std::mutex> lock{get_handler_mutex_};
...
2. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field.h - Lines:
354-395
354: /// \brief Private delegating ctor - EnableSet=true, EnableGet=true.
355: template <typename SampleDataType, bool EnableSet, bool EnableNotifier, bool EnableGet>
356: template <bool ES, bool EG, typename>
357: SkeletonField<SampleDataType, EnableSet, EnableNotifier, EnableGet>::SkeletonField(
358: SkeletonBase& parent,
359: std::unique_ptr<SkeletonEvent<FieldType>> skeleton_event_dispatch,
360: std::unique_ptr<SkeletonMethod<Result<FieldType>(FieldType)>> set_method,
361: std::unique_ptr<SkeletonMethod<Result<FieldType>()>> get_method,
362: const std::string_view field_name)
363: : SkeletonFieldBase{parent, field_name, std::move(skeleton_event_dispatch)},
...
3. File Lookup
- Filename:
skeleton_method.h
Found 3 file(s) matching 'skeleton_method.h':
- score/mw/com/impl/bindings/lola/skeleton_method.h
- score/mw/com/impl/bindings/mock_binding/skeleton_method.h
- score/mw/com/impl/methods/skeleton_method.h
4. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Lines:
1-100
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
5. File Snapshot (HEAD)
- File:
score/mw/com/impl/methods/skeleton_method.h
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
6. File Lookup
- Filename:
skeleton_method_binding.h
Found 1 file(s) matching 'skeleton_method_binding.h':
- score/mw/com/impl/methods/skeleton_method_binding.h
7. File Snapshot (HEAD)
- File:
score/mw/com/impl/skeleton_field_test.cpp - Line:
900
900: // When the initial value is set via an Update call
901: const auto update_result_2 = unit_2.my_dummy_field_.Update(initial_value_2);
902:
903: // which does not return an error
904: EXPECT_TRUE(update_result_2.has_value());
905:
906: unit_2 = std::move(unit);
907:
908: // and PrepareOffer() is called on the field in the new skeleton
909: const auto prepare_offer_result = unit_2.my_dummy_field_.PrepareOffer();
...
8. File Snapshot (HEAD)
- File:
score/mw/com/impl/methods/skeleton_method_binding.h
1: /********************************************************************************
2: * Copyright (c) 2025 Contributors to the Eclipse Foundation
3: *
4: * See the NOTICE file(s) distributed with this work for additional
5: * information regarding copyright ownership.
6: *
7: * This program and the accompanying materials are made available under the
8: * terms of the Apache License Version 2.0 which is available at
9: * https://www.apache.org/licenses/LICENSE-2.0
10: *
...
Please note that this PR is rebased on
eclipse-score#204
https://cc-github.bmwgroup.net/oss/eclipse-score-communication/pull/175/
These PRs needs to be merged before this PR.
Muse skeleton: Implement Get functionality for SkeletonField
This pull request introduces the
EnableGettemplate flag to theSkeletonFieldclass, enabling the implementation of 'Get' function functionality. It updates theSkeletonFieldconstructors to support various combinations ofEnableSetandEnableGetflags using SFINAE and tag dispatching. ARegisterGetHandlermethod is added toSkeletonFieldto automatically register a handler that retrieves the latest field sample. Correspondingly, aGetLatestSamplemethod is added toSkeletonEventBindingandSkeletonEvent, and theEventDataControlCompositeImplis extended withFindLatestReadableSlotIndexandTryIncreaseReferenceCountto support theGetLatestSlotfunctionality. Astd::mutexis conditionally included inSkeletonFieldto serialize concurrent 'Get' requests.Architecture Diagram:
flowchart TD subgraph API_Layer A["SkeletonField (+752 lines)"] B["SkeletonEvent (+8 lines)"] end subgraph Service_Layer C["SkeletonEventBinding (+2 lines)"] D["SkeletonMethod (+1 line)"] end subgraph Binding_Layer E["EventDataControlCompositeImpl (+52 lines)"] F["EventDataControl"] end subgraph Core_Components G["SkeletonBase"] H["SkeletonTrait"] end A -- uses --> B: "dispatches to" A -- creates --> D: "for Get/Set" B -- implements --> C C -- uses --> E: "for latest sample" E -- manages --> F: "controls slots" G -- contains --> A: "registers fields" H -- defines --> A: "template alias" A -- locks --> A_mutex["std::mutex (GetHandlerMutexType)"]This summary was automatically generated by @propel-code-bot