Skip to content
Draft
2 changes: 2 additions & 0 deletions score/mw/com/impl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ cc_library(
"//score/mw/com/impl/mocking:i_skeleton_event",
"//score/mw/com/impl/plumbing:event",
"//score/mw/com/impl/plumbing:sample_allocatee_ptr",
"//score/mw/com/impl/plumbing:sample_ptr",
"//score/mw/com/impl/tracing:skeleton_event_tracing",
"@score_baselibs//score/mw/log",
"@score_baselibs//score/result",
Expand Down Expand Up @@ -534,6 +535,7 @@ cc_library(
deps = [
":binding_type",
"//score/mw/com/impl/plumbing:sample_allocatee_ptr",
"//score/mw/com/impl/plumbing:sample_ptr",
"//score/mw/com/impl/tracing:skeleton_event_tracing_data",
"@score_baselibs//score/language/futurecpp",
"@score_baselibs//score/result",
Expand Down
1 change: 1 addition & 0 deletions score/mw/com/impl/bindings/lola/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ cc_library(
"//score/mw/com/impl/configuration",
"//score/mw/com/impl/methods:skeleton_method_binding",
"//score/mw/com/impl/plumbing:sample_allocatee_ptr",
"//score/mw/com/impl/plumbing:sample_ptr",
"//score/mw/com/impl/tracing:skeleton_event_tracing",
"//score/mw/com/impl/util:arithmetic_utils",
"@score_baselibs//score/filesystem",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,59 @@ auto ConsumerEventDataControlLocalView<AtomicIndirectorType>::ReferenceNextEvent
return {};
}

template <template <class> class AtomicIndirectorType>
auto ConsumerEventDataControlLocalView<AtomicIndirectorType>::GetLatestSlot() noexcept -> std::optional<SlotIndexType>
{
for (std::size_t retry_counter{0U}; retry_counter < MAX_REFERENCE_RETRIES; ++retry_counter)
{
EventSlotStatus::EventTimeStamp latest_time_stamp{EventSlotStatus::FIRST_VALID_TIMESTAMP};
std::optional<SlotIndexType> latest_slot_index{};
EventSlotStatus latest_slot_status{};

SlotIndexType current_index = 0U;
for (const auto& slot : state_slots_)
{
const EventSlotStatus slot_status{slot.load(std::memory_order_acquire)};
if (!slot_status.IsInvalid() && !slot_status.IsInWriting())
{
const auto slot_time_stamp = slot_status.GetTimeStamp();
if (latest_time_stamp < slot_time_stamp)
{
latest_time_stamp = slot_time_stamp;
latest_slot_index = current_index;
latest_slot_status = slot_status;
}
}
++current_index;
}

if (!latest_slot_index.has_value())
{
return {};
}

const auto slot_index = latest_slot_index.value();
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD(static_cast<std::size_t>(slot_index) < state_slots_.size());

auto expected = static_cast<EventSlotStatus::value_type>(latest_slot_status);
const auto new_refcount =
static_cast<EventSlotStatus::SubscriberCount>(latest_slot_status.GetReferenceCount() + 1U);
const EventSlotStatus updated_slot_status{latest_slot_status.GetTimeStamp(), new_refcount};
const auto desired = static_cast<EventSlotStatus::value_type>(updated_slot_status);

transaction_log_local_view_->ReferenceTransactionBegin(slot_index);
if (AtomicIndirectorType<EventSlotStatus::value_type>::compare_exchange_weak(
state_slots_[slot_index], expected, desired, std::memory_order_acq_rel))
{
transaction_log_local_view_->ReferenceTransactionCommit(slot_index);
return latest_slot_index;
}
transaction_log_local_view_->ReferenceTransactionAbort(slot_index);
}

return {};
}

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". std::terminate() is implicitly called from 'state_slots_[]' which might leds to a segmentation fault
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

namespace score::mw::com::impl::lola
{
template <typename SampleType>
class SkeletonEventCommon;

/// \brief View class which provides functionality for interacting with EventDataControl.
///
Expand Down Expand Up @@ -68,6 +70,13 @@ class ConsumerEventDataControlLocalView final
~ConsumerEventDataControlLocalView() noexcept = default;

ConsumerEventDataControlLocalView(const ConsumerEventDataControlLocalView&) = delete;

// Suppress "AUTOSAR C++14 A11-3-1"
// SkeletonEventCommon needs controlled access to transaction-log view injection/cleanup for dual quality
// consumer setup.
// coverity[autosar_cpp14_a11_3_1_violation]
template <typename SampleType>
friend class SkeletonEventCommon;
ConsumerEventDataControlLocalView& operator=(const ConsumerEventDataControlLocalView&) = delete;
ConsumerEventDataControlLocalView(ConsumerEventDataControlLocalView&&) noexcept = delete;
ConsumerEventDataControlLocalView& operator=(ConsumerEventDataControlLocalView&& other) noexcept = delete;
Expand All @@ -87,6 +96,15 @@ class ConsumerEventDataControlLocalView final
const EventSlotStatus::EventTimeStamp last_search_time,
const EventSlotStatus::EventTimeStamp upper_limit = EventSlotStatus::TIMESTAMP_MAX) noexcept;

/// \brief Returns the latest readable sample and marks it as referenced.
///
/// \details This method performs bounded retries on data races while increasing the reference count for the
/// selected slot.
///
/// \return Index of latest sample if available, empty optional otherwise.
/// \post DereferenceEvent() is invoked to withdraw read-ownership
std::optional<SlotIndexType> GetLatestSlot() noexcept;

/// \brief Increments refcount of given slot by one (given it is in the correct state i.e. being accessible/
/// readable)
/// \details This is a specific feature - not used by the standard proxy/consumer, which is using
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,38 @@ TEST_F(ConsumerEventDataControlLocalViewFixture, FailingToUpdateSlotValueCausesR
// No event will be found
ASSERT_FALSE(event.has_value());
}

TEST_F(ConsumerEventDataControlLocalViewFixture, GetLatestSlotReturnsNewestReadySlot)
{
// Given an EventDataControl with multiple ready slots and increasing timestamps
GivenAConsumerEventDataControlLocalViewUsingRealAtomics(3);
score::cpp::ignore = WithAnAllocatedSlot(1);
score::cpp::ignore = WithAnAllocatedSlot(2);
score::cpp::ignore = WithAnAllocatedSlot(3);

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

// Then the newest sample is returned and referenced
ASSERT_TRUE(latest_slot.has_value());
EXPECT_EQ((*unit_)[latest_slot.value()].GetTimeStamp(), 3U);
EXPECT_EQ((*unit_)[latest_slot.value()].GetReferenceCount(), 1U);
}

TEST_F(ConsumerEventDataControlLocalViewFixture, GetLatestSlotReturnsNullIfNoReadySlotExists)
{
// Given an EventDataControl with one slot in writing state (allocated but not marked ready)
GivenAConsumerEventDataControlLocalViewUsingRealAtomics(1);
const auto allocated_slot = provider_event_data_control_local_->AllocateNextSlot();
ASSERT_TRUE(allocated_slot.has_value());

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

// Then no slot is returned
EXPECT_FALSE(latest_slot.has_value());
}

using EventDataControlReferenceSpecificEventFixture = ConsumerEventDataControlLocalViewFixture;
TEST_F(EventDataControlReferenceSpecificEventFixture, ReferenceSpecificEvents)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ template <template <class> class AtomicIndirectorType>
// coverity[autosar_cpp14_a15_5_3_violation : FALSE]
EventSlotStatus::EventTimeStamp EventDataControlComposite<AtomicIndirectorType>::GetLatestTimestamp() const noexcept
{
EventSlotStatus::EventTimeStamp latest_time_stamp{1U};
EventSlotStatus::EventTimeStamp latest_time_stamp{EventSlotStatus{}.GetTimeStamp()};
auto& control = (asil_b_control_local_ != nullptr) ? *asil_b_control_local_ : asil_qm_control_local_.get();
for (SlotIndexType slot_index = 0U;
// Suppress "AUTOSAR C++14 A4-7-1" rule finding. This rule states: "An integer expression shall not lead to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,9 +284,9 @@ TEST_F(EventDataControlCompositeFixture, GetLatestTimeStampReturnsDefaultValuesI
unit_->Discard(allocation_result.allocated_slot_index.value());
unit_->Discard(allocation_result_2.allocated_slot_index.value());

// Then the timestamp equal to 1
// Then the timestamp is invalid
auto time_stamp = unit_->GetLatestTimestamp();
EventSlotStatus::EventTimeStamp expected_time_stamp{1};
EventSlotStatus::EventTimeStamp expected_time_stamp{EventSlotStatus{}.GetTimeStamp()};
EXPECT_EQ(time_stamp, expected_time_stamp);
}

Expand All @@ -295,9 +295,9 @@ TEST_F(EventDataControlCompositeFixture, GetLatestTimeStampReturnsDefaultValuesI
// Given an EventDataControlComposite with zero used slots
WithQmAndAsilBEventDataControlCompositeUsingRealAtomics();

// Then the timestamp equal to 1
// Then the timestamp is invalid
auto time_stamp = unit_->GetLatestTimestamp();
EventSlotStatus::EventTimeStamp expected_time_stamp{1};
EventSlotStatus::EventTimeStamp expected_time_stamp{EventSlotStatus{}.GetTimeStamp()};
EXPECT_EQ(time_stamp, expected_time_stamp);
}

Expand All @@ -310,9 +310,9 @@ TEST_F(EventDataControlCompositeFixture,
// When allocating 1 slot
score::cpp::ignore = unit_->AllocateNextSlot();

// Then the timestamp equal to 1
// Then the timestamp is invalid
auto time_stamp = unit_->GetLatestTimestamp();
EventSlotStatus::EventTimeStamp expected_time_stamp{1};
EventSlotStatus::EventTimeStamp expected_time_stamp{EventSlotStatus{}.GetTimeStamp()};
EXPECT_EQ(time_stamp, expected_time_stamp);
}

Expand Down
10 changes: 8 additions & 2 deletions score/mw/com/impl/bindings/lola/event_slot_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,10 @@ class EventSlotStatus final
// coverity[autosar_cpp14_a0_1_1_violation : FALSE]
static constexpr EventTimeStamp TIMESTAMP_MAX = std::numeric_limits<EventTimeStamp>::max();

/// \brief If default constructed, SlotStatus is invalid
EventSlotStatus() noexcept = default;
/// \brief First valid timestamp value that can appear in shared memory.
static constexpr EventTimeStamp FIRST_VALID_TIMESTAMP{1U};

EventSlotStatus() noexcept : EventSlotStatus{UNINITIALIZED_TIMESTAMP} {}
explicit EventSlotStatus(const value_type init_val) noexcept;
EventSlotStatus(const EventTimeStamp timestamp, const SubscriberCount refcount) noexcept;
EventSlotStatus(const EventSlotStatus&) noexcept = default;
Expand Down Expand Up @@ -83,9 +85,13 @@ class EventSlotStatus final
bool IsTimeStampBetween(const EventTimeStamp min_timestamp, const EventTimeStamp max_timestamp) const noexcept;

private:
/// \brief Timestamp value indicating that a slot was never written.
static constexpr EventTimeStamp UNINITIALIZED_TIMESTAMP{0U};

value_type data_;
};

static_assert(sizeof(EventSlotStatus) <= sizeof(std::uint64_t));
} // namespace score::mw::com::impl::lola

#endif // SCORE_MW_COM_IMPL_BINDINGS_LOLA_EVENT_SLOT_STATUS_H
3 changes: 3 additions & 0 deletions score/mw/com/impl/bindings/lola/generic_skeleton_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class GenericSkeletonEvent : public GenericSkeletonEventBinding
skeleton_event_common_.SetSkeletonEventTracingData(tracing_data);
}

// GenericSkeletonEvent does not support getter functionality (void type); no-op.
void SetGetterEnabled(bool /*getter_enabled*/) noexcept override {}

std::size_t GetMaxSize() const noexcept override
{
return size_info_.size;
Expand Down
38 changes: 38 additions & 0 deletions score/mw/com/impl/bindings/lola/skeleton_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,25 @@
#include "score/mw/com/impl/bindings/lola/i_runtime.h"
#include "score/mw/com/impl/bindings/lola/provider_event_data_control_local_view.h"
#include "score/mw/com/impl/bindings/lola/sample_allocatee_ptr.h"
#include "score/mw/com/impl/bindings/lola/sample_ptr.h"
#include "score/mw/com/impl/bindings/lola/skeleton.h"
#include "score/mw/com/impl/bindings/lola/skeleton_event_common.h"
#include "score/mw/com/impl/bindings/lola/skeleton_event_properties.h"
#include "score/mw/com/impl/com_error.h"
#include "score/mw/com/impl/configuration/quality_type.h"
#include "score/mw/com/impl/plumbing/sample_allocatee_ptr.h"
#include "score/mw/com/impl/plumbing/sample_ptr.h"
#include "score/mw/com/impl/runtime.h"
#include "score/mw/com/impl/skeleton_event_binding.h"
#include "score/mw/com/impl/tracing/skeleton_event_tracing_data.h"

#include "score/mw/log/logging.h"

#include "score/result/result.h"
#include <score/assert.hpp>
#include <score/utility.hpp>

#include <cstdint>
#include <optional>
#include <string_view>
#include <utility>
Expand Down Expand Up @@ -87,6 +93,8 @@ class SkeletonEvent final : public SkeletonEventBinding<SampleType>

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

Result<impl::SamplePtr<SampleType>> GetLatestSample(const QualityType& quality_type) noexcept override;

/// @requirement SWS_CM_00700
Result<void> PrepareOffer() noexcept override;

Expand All @@ -102,6 +110,11 @@ class SkeletonEvent final : public SkeletonEventBinding<SampleType>
skeleton_event_common_.SetSkeletonEventTracingData(tracing_data);
}

void SetGetterEnabled(bool getter_enabled) noexcept override
{
skeleton_event_common_.SetGetterEnabled(getter_enabled);
}

private:
EventDataStorage<SampleType>* event_data_storage_;
SkeletonEventCommon<SampleType> skeleton_event_common_;
Expand Down Expand Up @@ -179,6 +192,31 @@ Result<impl::SampleAllocateePtr<SampleType>> SkeletonEvent<SampleType>::Allocate
slot_index));
}

template <typename SampleType>
Result<impl::SamplePtr<SampleType>> SkeletonEvent<SampleType>::GetLatestSample(const QualityType& quality_type) noexcept
{
if (event_data_storage_ == nullptr)
{
::score::mw::log::LogError("lola")
<< "Tried to get latest event sample, but the event has not been offered yet!";
return MakeUnexpected(ComErrc::kBindingFailure);
}

auto& consumer_event_data_control_local = skeleton_event_common_.GetConsumerEventDataControlLocalView(quality_type);
const auto slot_result = consumer_event_data_control_local.GetLatestSlot();
if (!slot_result.has_value())
{
::score::mw::log::LogError("lola") << "GetLatestSlot did not return a slot index";
return MakeUnexpected(ComErrc::kBindingFailure);
}

return impl::SamplePtr<SampleType>{
lola::SamplePtr<SampleType>{&event_data_storage_->at(static_cast<std::uint64_t>(*slot_result)),
consumer_event_data_control_local,
slot_result.value()},
impl::SampleReferenceGuard{}};
}

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 Down
Loading
Loading