Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,55 @@ EventDataControlCompositeImpl<AtomicIndirectorType>::EventDataControlCompositeIm
CheckForValidDataControls();
}

template <template <class> class AtomicIndirectorType>
score::cpp::optional<SlotIndexType> EventDataControlCompositeImpl<AtomicIndirectorType>::FindLatestReadableSlotIndex(
const EventDataControl& control) const noexcept
{
EventSlotStatus::EventTimeStamp latest_time_stamp{1U};
score::cpp::optional<SlotIndexType> latest_slot_index{};

for (SlotIndexType slot_index = 0U;
// Suppress "AUTOSAR C++14 A4-7-1" rule finding. This rule states: "An integer expression shall not lead
// to loss.". As the maximum number of slots is std::uint16_t, so there is no case for a data loss here.
// coverity[autosar_cpp14_a4_7_1_violation]
slot_index < static_cast<SlotIndexType>(control.state_slots_.size());
++slot_index)
{
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(static_cast<std::size_t>(slot_index) < control.state_slots_.size());
const EventSlotStatus slot{control.state_slots_[slot_index].load(std::memory_order_acquire)};
if (!slot.IsInvalid() && !slot.IsInWriting())
{
const auto slot_time_stamp = slot.GetTimeStamp();
if (latest_time_stamp < slot_time_stamp)
{
latest_time_stamp = slot_time_stamp;
latest_slot_index = slot_index;
}
}
}

return latest_slot_index;
}

template <template <class> class AtomicIndirectorType>
bool EventDataControlCompositeImpl<AtomicIndirectorType>::TryIncreaseReferenceCount(
ControlSlotType& slot) const noexcept
{
EventSlotStatus slot_old{slot.load(std::memory_order_acquire)};
if (slot_old.IsInvalid() || slot_old.IsInWriting())
{
return false;
}

const auto new_refcount = static_cast<EventSlotStatus::SubscriberCount>(slot_old.GetReferenceCount() + 1U);
EventSlotStatus slot_new{slot_old.GetTimeStamp(), new_refcount};

auto slot_old_value = static_cast<EventSlotStatus::value_type&>(slot_old);
auto slot_new_value = static_cast<EventSlotStatus::value_type&>(slot_new);
return AtomicIndirectorType<EventSlotStatus::value_type>::compare_exchange_weak(
slot, slot_old_value, slot_new_value, std::memory_order_acq_rel);
}

template <template <class> class AtomicIndirectorType>
// Suppress "AUTOSAR C++14 A15-5-3" rule findings. This rule states: "The std::terminate() function shall not be called
// implicitly". This is a false positive, all results which are accessed with '.value()' that could implicitly call
Expand Down Expand Up @@ -303,6 +352,53 @@ EventSlotStatus::EventTimeStamp EventDataControlCompositeImpl<AtomicIndirectorTy
}
}

template <template <class> class AtomicIndirectorType>
ControlSlotCompositeIndicator EventDataControlCompositeImpl<AtomicIndirectorType>::GetLatestSlot() const noexcept
{
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 {};
}

const auto slot_index = latest_slot_index.value();

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};
}

auto& slot_qm = asil_qm_control_->state_slots_[slot_index];
if (!TryIncreaseReferenceCount(slot_qm))
{
continue;
}

return {slot_index, slot_qm, ControlSlotCompositeIndicator::CompositeSlotTagType::QM};
}

return {};
}

template <template <class> class AtomicIndirectorType>
void EventDataControlCompositeImpl<AtomicIndirectorType>::CheckForValidDataControls() const noexcept
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ class EventDataControlCompositeImpl
/// \brief Returns the timestamp of the provided slot index
EventSlotStatus::EventTimeStamp GetEventSlotTimestamp(const SlotIndexType slot) const noexcept;

ControlSlotCompositeIndicator GetLatestSlot() const noexcept;

EventSlotStatus::EventTimeStamp GetLatestTimestamp() const noexcept;

private:
Expand All @@ -110,8 +112,10 @@ class EventDataControlCompositeImpl

// Algorithms that operate on multiple control blocks
ControlSlotCompositeIndicator GetNextFreeMultiSlot() const noexcept;
score::cpp::optional<SlotIndexType> FindLatestReadableSlotIndex(const EventDataControl& control) const noexcept;

bool TryLockSlot(ControlSlotCompositeIndicator slot_indicator) noexcept;
bool TryIncreaseReferenceCount(ControlSlotType& slot) const noexcept;
ControlSlotCompositeIndicator AllocateNextMultiSlot() noexcept;
void CheckForValidDataControls() const noexcept;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,73 @@ TEST_F(EventDataControlCompositeFixture, GetLatestTimeStampReturnCorrectValue)
EXPECT_EQ(time_stamp_obtained, time_stamp);
}

TEST_F(EventDataControlCompositeFixture, GetLatestSlotReturnsLatestReadySlotForQmOnly)
{
// Given an EventDataControlComposite with QM-only control and two ready slots
WithQmOnlyEventDataControl().WithRealEventDataControlComposite();

auto first_slot = unit_->AllocateNextSlot();
auto second_slot = unit_->AllocateNextSlot();
unit_->EventReady(first_slot, 2U);
unit_->EventReady(second_slot, 3U);

// When acquiring the latest slot
const auto latest_slot = unit_->GetLatestSlot();

// Then the slot with the highest timestamp is returned
ASSERT_TRUE(latest_slot.IsValidQM());
EXPECT_EQ(latest_slot.GetIndex(), second_slot.GetIndex());
}

TEST_F(EventDataControlCompositeFixture, GetLatestSlotIncrementsReferenceCountForQmOnly)
{
// Given an EventDataControlComposite with QM-only control and one ready slot
WithQmOnlyEventDataControl().WithRealEventDataControlComposite();

auto slot = unit_->AllocateNextSlot();
unit_->EventReady(slot, 2U);

// When acquiring the latest slot
const auto latest_slot = unit_->GetLatestSlot();

// Then the slot is referenced once
ASSERT_TRUE(latest_slot.IsValidQM());
const EventSlotStatus slot_status{latest_slot.GetSlotQM().load(std::memory_order_acquire)};
EXPECT_EQ(slot_status.GetReferenceCount(), 1U);
}

TEST_F(EventDataControlCompositeFixture, GetLatestSlotIncrementsReferenceCountForQmAndAsilB)
{
// Given an EventDataControlComposite with QM and ASIL-B controls and one ready slot
WithQmAndAsilBEventDataControls().WithRealEventDataControlComposite();

auto slot = unit_->AllocateNextSlot();
unit_->EventReady(slot, 2U);

// When acquiring the latest slot
const auto latest_slot = unit_->GetLatestSlot();

// Then both control parts are referenced once
ASSERT_TRUE(latest_slot.IsValidQmAndAsilB());
const EventSlotStatus qm_slot_status{latest_slot.GetSlotQM().load(std::memory_order_acquire)};
const EventSlotStatus asil_b_slot_status{latest_slot.GetSlotAsilB().load(std::memory_order_acquire)};
EXPECT_EQ(qm_slot_status.GetReferenceCount(), 1U);
EXPECT_EQ(asil_b_slot_status.GetReferenceCount(), 1U);
}

TEST_F(EventDataControlCompositeFixture, GetLatestSlotReturnsInvalidIndicatorIfNoReadableSlotExists)
{
// Given an EventDataControlComposite with no ready slot
WithQmAndAsilBEventDataControls().WithRealEventDataControlComposite();

// When acquiring the latest slot
const auto latest_slot = unit_->GetLatestSlot();

// Then no valid slot is returned
EXPECT_FALSE(latest_slot.IsValidQM());
EXPECT_FALSE(latest_slot.IsValidAsilB());
}

TEST_F(EventDataControlCompositeFixture, CanAllocateMultipleSlots)
{
// Given an EventDataControlComposite with zero used slots
Expand Down
48 changes: 43 additions & 5 deletions score/mw/com/impl/bindings/lola/skeleton_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,16 @@ class SkeletonEvent final : public SkeletonEventBinding<SampleType>

/// \brief Sends a value by _copy_ towards a consumer. It will allocate the necessary space and then copy the value
/// into Shared Memory.
ResultBlank Send(const SampleType& value, score::cpp::optional<SendTraceCallback> send_trace_callback) noexcept override;
ResultBlank Send(const SampleType& value,
score::cpp::optional<SendTraceCallback> send_trace_callback) noexcept override;

ResultBlank Send(impl::SampleAllocateePtr<SampleType> sample,
score::cpp::optional<SendTraceCallback> send_trace_callback) noexcept override;

Result<impl::SampleAllocateePtr<SampleType>> Allocate() noexcept override;

Result<SampleType> GetLatestSample() noexcept override;

/// @requirement SWS_CM_00700
ResultBlank PrepareOffer() noexcept override;

Expand Down Expand Up @@ -232,7 +235,8 @@ Result<impl::SampleAllocateePtr<SampleType>> SkeletonEvent<SampleType>::Allocate
{
if (event_data_control_composite_.has_value() == false)
{
::score::mw::log::LogError("lola") << "Tried to allocate event, but the EventDataControl does not exist!";
::score::mw::log::LogError("lola")
<< "Tried to allocate event, but the EventDataControlComposite does not exist!";
return MakeUnexpected(ComErrc::kBindingFailure);
}
const auto slot = event_data_control_composite_->AllocateNextSlot();
Expand Down Expand Up @@ -271,6 +275,38 @@ Result<impl::SampleAllocateePtr<SampleType>> SkeletonEvent<SampleType>::Allocate
}
}

template <typename SampleType>
Result<SampleType> SkeletonEvent<SampleType>::GetLatestSample() noexcept
{
if (event_data_control_composite_.has_value() == false)
{
::score::mw::log::LogError("lola")
<< "Tried to get the latest sample, but the EventDataControlComposite does not exist!";
return MakeUnexpected(ComErrc::kBindingFailure);
}
auto slot = event_data_control_composite_->GetLatestSlot();

if (slot.IsValidQM() || slot.IsValidAsilB())
{
const auto sample = event_data_storage_->at(static_cast<std::uint64_t>(slot.GetIndex()));

if (slot.IsValidQM())
{
score::cpp::ignore = slot.GetSlotQM().fetch_sub(1U, std::memory_order_acq_rel);
}
if (slot.IsValidAsilB())
{
score::cpp::ignore = slot.GetSlotAsilB().fetch_sub(1U, std::memory_order_acq_rel);
}

return sample;
}
else
{
return MakeUnexpected(ComErrc::kBindingFailure);
}
}

template <typename SampleType>
// Suppress "AUTOSAR C++14 A15-5-3" rule findings. This rule states: "The std::terminate() function shall not be called
// implicitly". This is a false positive, all results which are accessed with '.value()' that could implicitly call
Expand All @@ -283,8 +319,9 @@ ResultBlank SkeletonEvent<SampleType>::PrepareOffer() noexcept
std::tie(event_data_storage_, event_data_control_composite_) =
parent_.Register<SampleType>(event_fqn_, event_properties_);

SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(event_data_control_composite_.has_value(),
"Defensive programming as event_data_control_composite_ is set by Register above.");
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(
event_data_control_composite_.has_value(),
"Defensive programming as event_data_control_composite_ is set by Register above.");
current_timestamp_ = event_data_control_composite_.value().GetLatestTimestamp();

const bool tracing_for_skeleton_event_enabled =
Expand All @@ -298,7 +335,8 @@ ResultBlank SkeletonEvent<SampleType>::PrepareOffer() noexcept
// LCOV_EXCL_BR_STOP
score::cpp::ignore = transaction_log_registration_guard_.emplace(
TransactionLogRegistrationGuard::Create(event_data_control_composite_->GetQmEventDataControl()));
score::cpp::ignore = type_erased_sample_ptrs_guard_.emplace(skeleton_event_tracing_data_.service_element_tracing_data);
score::cpp::ignore =
type_erased_sample_ptrs_guard_.emplace(skeleton_event_tracing_data_.service_element_tracing_data);
}

// Register callbacks to be notified when event notification existence changes.
Expand Down
50 changes: 50 additions & 0 deletions score/mw/com/impl/bindings/lola/skeleton_event_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,56 @@ TEST_F(SkeletonEventPrepareStopOfferFixture, StopOfferSkeletonEvent)
skeleton_event_->PrepareStopOffer();
}

using SkeletonEventGetLatestSampleFixture = SkeletonEventFixture;
TEST_F(SkeletonEventGetLatestSampleFixture, GetLatestSampleFailsBeforePrepareOffer)
{
const bool enforce_max_samples{true};
InitialiseSkeletonEvent(fake_element_fq_id_, fake_event_name_, max_samples_, max_subscribers_, enforce_max_samples);

const auto latest_sample = skeleton_event_->GetLatestSample();

ASSERT_FALSE(latest_sample.has_value());
EXPECT_EQ(latest_sample.error(), ComErrc::kBindingFailure);
}

TEST_F(SkeletonEventGetLatestSampleFixture, GetLatestSampleFailsIfNoSampleWasSent)
{
const bool enforce_max_samples{true};
InitialiseSkeletonEvent(fake_element_fq_id_, fake_event_name_, max_samples_, max_subscribers_, enforce_max_samples);
std::ignore = skeleton_event_->PrepareOffer();

const auto latest_sample = skeleton_event_->GetLatestSample();

ASSERT_FALSE(latest_sample.has_value());
EXPECT_EQ(latest_sample.error(), ComErrc::kBindingFailure);
}

TEST_F(SkeletonEventGetLatestSampleFixture, GetLatestSampleReturnsMostRecentlySentSample)
{
const bool enforce_max_samples{true};
InitialiseSkeletonEvent(fake_element_fq_id_, fake_event_name_, max_samples_, max_subscribers_, enforce_max_samples);
std::ignore = skeleton_event_->PrepareOffer();

auto first_allocated_slot_result = skeleton_event_->Allocate();
ASSERT_TRUE(first_allocated_slot_result.has_value());
auto first_allocated_slot = std::move(first_allocated_slot_result).value();
*first_allocated_slot = static_cast<test::TestSampleType>(11U);
auto first_send_result = skeleton_event_->Send(std::move(first_allocated_slot), score::cpp::nullopt);
ASSERT_TRUE(first_send_result.has_value());

auto second_allocated_slot_result = skeleton_event_->Allocate();
ASSERT_TRUE(second_allocated_slot_result.has_value());
auto second_allocated_slot = std::move(second_allocated_slot_result).value();
*second_allocated_slot = static_cast<test::TestSampleType>(42U);
auto second_send_result = skeleton_event_->Send(std::move(second_allocated_slot), score::cpp::nullopt);
ASSERT_TRUE(second_send_result.has_value());

const auto latest_sample = skeleton_event_->GetLatestSample();

ASSERT_TRUE(latest_sample.has_value());
EXPECT_EQ(latest_sample.value(), static_cast<test::TestSampleType>(42U));
}

using SkeletonEventTimestampFixture = SkeletonEventFixture;
TEST_F(SkeletonEventTimestampFixture, SendUpdatesTimestampInControlData)
{
Expand Down
8 changes: 7 additions & 1 deletion score/mw/com/impl/bindings/mock_binding/skeleton_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ class SkeletonEvent : public SkeletonEventBinding<SampleType>
public:
MOCK_METHOD(ResultBlank,
Send,
(const SampleType& value, score::cpp::optional<typename SkeletonEventBinding<SampleType>::SendTraceCallback>),
(const SampleType& value,
score::cpp::optional<typename SkeletonEventBinding<SampleType>::SendTraceCallback>),
(noexcept, override));
MOCK_METHOD(ResultBlank,
Send,
(SampleAllocateePtr<SampleType> sample,
score::cpp::optional<typename SkeletonEventBinding<SampleType>::SendTraceCallback>),
(noexcept, override));
MOCK_METHOD(Result<SampleAllocateePtr<SampleType>>, Allocate, (), (noexcept, override));
MOCK_METHOD(Result<SampleType>, GetLatestSample, (), (noexcept, override));
MOCK_METHOD(ResultBlank, PrepareOffer, (), (noexcept, override));
MOCK_METHOD(void, PrepareStopOffer, (), (noexcept, override));
MOCK_METHOD(std::size_t, GetMaxSize, (), (const, noexcept, override));
Expand Down Expand Up @@ -82,6 +84,10 @@ class SkeletonEventFacade : public SkeletonEventBinding<SampleType>
{
return skeleton_event_.Allocate();
};
Result<SampleType> GetLatestSample() noexcept override
{
return skeleton_event_.GetLatestSample();
}
ResultBlank PrepareOffer() noexcept override
{
return skeleton_event_.PrepareOffer();
Expand Down
Loading
Loading