diff --git a/SilKit/IntegrationTests/ITest_MessageAggregation.cpp b/SilKit/IntegrationTests/ITest_MessageAggregation.cpp index 047383721..1df54f349 100644 --- a/SilKit/IntegrationTests/ITest_MessageAggregation.cpp +++ b/SilKit/IntegrationTests/ITest_MessageAggregation.cpp @@ -83,7 +83,7 @@ TEST_F(ITest_MessageAggregation, timeout_in_case_of_deadlock_when_using_async_si SilKit::Services::PubSub::PubSubSpec dataSpecPing{"ping", {}}; SilKit::Services::PubSub::PubSubSpec dataSpecPong{"pong", {}}; - bool msgReceived{false}; + std::atomic_bool msgReceived{false}; // participant with async simulation step handler & enabled message aggregation { @@ -105,8 +105,8 @@ TEST_F(ITest_MessageAggregation, timeout_in_case_of_deadlock_when_using_async_si }); timeSyncService->SetSimulationStepHandlerAsync( - [dataPublisher, lifecycleService, &msgReceived](std::chrono::nanoseconds /*now*/, - std::chrono::nanoseconds /*duration*/) { + [dataPublisher, lifecycleService, &msgReceived](std::chrono::nanoseconds, + std::chrono::nanoseconds) { // send ping std::vector ping(1, '?'); dataPublisher->Publish(std::move(ping)); @@ -135,7 +135,7 @@ TEST_F(ITest_MessageAggregation, timeout_in_case_of_deadlock_when_using_async_si }); timeSyncService->SetSimulationStepHandlerAsync( - [timeSyncService](std::chrono::nanoseconds /*now*/, std::chrono::nanoseconds /*duration*/) { + [timeSyncService](std::chrono::nanoseconds, std::chrono::nanoseconds) { timeSyncService->CompleteSimulationStep(); }, 1s); } diff --git a/SilKit/IntegrationTests/ITest_NetSimFlexRay.cpp b/SilKit/IntegrationTests/ITest_NetSimFlexRay.cpp index c96f39bf4..e78d7558b 100644 --- a/SilKit/IntegrationTests/ITest_NetSimFlexRay.cpp +++ b/SilKit/IntegrationTests/ITest_NetSimFlexRay.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: MIT #include "ITest_NetSim.hpp" +#include "ITestThreadSafeLogger.hpp" #include "silkit/services/flexray/all.hpp" namespace { @@ -23,8 +24,9 @@ struct ITest_NetSimFlexray : ITest_NetSim CallCountsSilKitHandlersFlexray& callCountsSilKitHandlersFlexray) { controller->AddCycleStartHandler( - [&callCountsSilKitHandlersFlexray](IFlexrayController*, const FlexrayCycleStartEvent& /*msg*/) { + [&callCountsSilKitHandlersFlexray](IFlexrayController*, const FlexrayCycleStartEvent& msg) { callCountsSilKitHandlersFlexray.CycleStartHandler++; + Log() << "Cycle Start: " << (int)msg.cycleCounter; }); controller->AddFrameHandler( [&callCountsSilKitHandlersFlexray](IFlexrayController*, const FlexrayFrameEvent& /*msg*/) { @@ -273,13 +275,14 @@ void MySimulatedFlexrayController::OnTxBufferUpdate( TEST_F(ITest_NetSimFlexray, basic_networksimulation_flexray) { + const auto configSynchronizationPoints = "EnableSynchronizationPoints: true"; { // ---------------------------- // NetworkSimulator // ---------------------------- //auto configWithLogging = MakeParticipantConfigurationStringWithLogging(SilKit::Services::Logging::Level::Info); - auto&& simParticipant = _simTestHarness->GetParticipant(_participantNameNetSim); + auto&& simParticipant = _simTestHarness->GetParticipant(_participantNameNetSim, configSynchronizationPoints); auto&& lifecycleService = simParticipant->GetOrCreateLifecycleService(); auto&& timeSyncService = simParticipant->GetOrCreateTimeSyncService(); auto&& networkSimulator = simParticipant->GetOrCreateNetworkSimulator(); @@ -307,7 +310,8 @@ TEST_F(ITest_NetSimFlexray, basic_networksimulation_flexray) timeSyncService->SetSimulationStepHandler( [this, simulatedNetworkPtr, lifecycleService, flexrayController]( - auto now, const std::chrono::nanoseconds /*duration*/) { + auto now, const std::chrono::nanoseconds duration) { + (void)duration; if (now == _stopAtMs) { lifecycleService->Stop("stopping the simulation"); @@ -328,7 +332,7 @@ TEST_F(ITest_NetSimFlexray, basic_networksimulation_flexray) for (const auto& participantName : _participantNamesSimulated) { - auto&& simParticipant = _simTestHarness->GetParticipant(participantName); + auto&& simParticipant = _simTestHarness->GetParticipant(participantName, configSynchronizationPoints); auto&& lifecycleService = simParticipant->GetOrCreateLifecycleService(); auto&& timeSyncService = simParticipant->GetOrCreateTimeSyncService(); @@ -337,8 +341,9 @@ TEST_F(ITest_NetSimFlexray, basic_networksimulation_flexray) SetupFlexrayController(lifecycleService, flexrayController, callCounts.silKitHandlersFlexray); timeSyncService->SetSimulationStepHandler( - [this, flexrayController](auto now, const std::chrono::nanoseconds /*duration*/) { + [this, flexrayController](auto now, const std::chrono::nanoseconds duration) { OnetimeActions(now, flexrayController); + Log() << "Simulation step: " << now.count() << " : " << duration.count(); }, _stepSize); } } diff --git a/SilKit/source/config/ParticipantConfiguration.hpp b/SilKit/source/config/ParticipantConfiguration.hpp index e1322cb46..f8b20b557 100644 --- a/SilKit/source/config/ParticipantConfiguration.hpp +++ b/SilKit/source/config/ParticipantConfiguration.hpp @@ -379,6 +379,8 @@ struct ParticipantConfiguration : public IParticipantConfiguration Includes includes; Middleware middleware; Experimental experimental; + // experimental synchronization points + bool enableSynchronizationPoints{false}; }; bool operator==(const CanController& lhs, const CanController& rhs); diff --git a/SilKit/source/config/ParticipantConfigurationFromXImpl.cpp b/SilKit/source/config/ParticipantConfigurationFromXImpl.cpp index d2e3f7c20..b545fb372 100644 --- a/SilKit/source/config/ParticipantConfigurationFromXImpl.cpp +++ b/SilKit/source/config/ParticipantConfigurationFromXImpl.cpp @@ -96,6 +96,7 @@ struct ConfigIncludeData std::map rpcClientCache; std::map traceSinkCache; std::map traceSourceCache; + bool enableSynchronizationPoints; }; @@ -550,6 +551,8 @@ void MergeExperimentalCache(const ExperimentalCache& cache, Experimental& experi auto MergeConfigs(ConfigIncludeData& configIncludeData) -> SilKit::Config::ParticipantConfiguration { SilKit::Config::ParticipantConfiguration config; + config.enableSynchronizationPoints = configIncludeData.enableSynchronizationPoints; + for (const auto& include : configIncludeData.configBuffer) { // Merge all vectors first! @@ -666,6 +669,8 @@ auto PaticipantConfigurationWithIncludes(const std::string& text, struct ConfigI throw SilKit::ConfigurationError{fmt::format("Unknown schema version '{}' found in participant configuration!", configuration.schemaVersion)}; } + configData.enableSynchronizationPoints = configuration.enableSynchronizationPoints; + configData.configBuffer.push_back(ConfigInclude("root", configuration)); AppendToSearchPaths(configuration, configData); diff --git a/SilKit/source/config/YamlReader.cpp b/SilKit/source/config/YamlReader.cpp index f53c07f94..cfce9ca4a 100644 --- a/SilKit/source/config/YamlReader.cpp +++ b/SilKit/source/config/YamlReader.cpp @@ -484,6 +484,9 @@ void YamlReader::Read(SilKit::Config::ParticipantConfiguration& obj) OptionalRead(obj.middleware, "Middleware"); OptionalRead(obj.includes, "Includes"); OptionalRead(obj.experimental, "Experimental"); + + // design proposal + OptionalRead(obj.enableSynchronizationPoints, "EnableSynchronizationPoints"); } void YamlReader::Read(SilKit::Config::HealthCheck& obj) diff --git a/SilKit/source/config/YamlWriter.cpp b/SilKit/source/config/YamlWriter.cpp index 6e817aa84..7960b7603 100644 --- a/SilKit/source/config/YamlWriter.cpp +++ b/SilKit/source/config/YamlWriter.cpp @@ -600,6 +600,9 @@ void YamlWriter::Write(const SilKit::Config::ParticipantConfiguration& obj) NonDefaultWrite(obj.middleware, "Middleware", defaultObj.middleware); NonDefaultWrite(obj.includes, "Includes", defaultObj.includes); NonDefaultWrite(obj.experimental, "Experimental", defaultObj.experimental); + + //design proposal + NonDefaultWrite(obj.enableSynchronizationPoints, "EnableSynchronizationPoints", defaultObj.enableSynchronizationPoints); } void YamlWriter::Write(const SilKit::Config::HealthCheck& obj) diff --git a/SilKit/source/core/internal/IParticipantInternal.hpp b/SilKit/source/core/internal/IParticipantInternal.hpp index 3da426d3d..1c7b9e7cb 100644 --- a/SilKit/source/core/internal/IParticipantInternal.hpp +++ b/SilKit/source/core/internal/IParticipantInternal.hpp @@ -330,6 +330,8 @@ class IParticipantInternal : public IParticipant virtual auto GetMetricsProcessor() -> IMetricsProcessor* = 0; virtual auto GetMetricsSender() -> IMetricsSender* = 0; + + virtual auto GetConfiguration() -> const Config::ParticipantConfiguration& = 0; }; } // namespace Core diff --git a/SilKit/source/core/internal/OrchestrationDatatypes.hpp b/SilKit/source/core/internal/OrchestrationDatatypes.hpp index 109dc8340..abafc2187 100644 --- a/SilKit/source/core/internal/OrchestrationDatatypes.hpp +++ b/SilKit/source/core/internal/OrchestrationDatatypes.hpp @@ -21,6 +21,12 @@ struct NextSimTask std::chrono::nanoseconds duration{0}; }; +static constexpr NextSimTask ZeroSimTask{std::chrono::nanoseconds{0}, std::chrono::nanoseconds{0}}; +inline auto operator==(const NextSimTask& lhs, const NextSimTask& rhs) +{ + return lhs.duration == rhs.duration && lhs.timePoint == rhs.timePoint; +} + //! System-wide command for the simulation flow. struct SystemCommand { diff --git a/SilKit/source/core/internal/traits/SilKitMsgTraits.hpp b/SilKit/source/core/internal/traits/SilKitMsgTraits.hpp index f04e83f31..8c586482c 100644 --- a/SilKit/source/core/internal/traits/SilKitMsgTraits.hpp +++ b/SilKit/source/core/internal/traits/SilKitMsgTraits.hpp @@ -14,6 +14,8 @@ namespace Core { // ================================================================== // Trait which checks that '.timestamp' works // ================================================================== +template +using RemoveCvRef = std::remove_cv_t>; template struct HasTimestamp : std::false_type @@ -58,6 +60,15 @@ struct SilKitMsgTraitForbidSelfDelivery } }; +template +struct SilKitMsgTraitIsSynchronizationPoint +{ + static constexpr bool IsSynchronizationPoint() + { + return false; + } +}; + // The final message traits template struct SilKitMsgTraits @@ -67,6 +78,7 @@ struct SilKitMsgTraits , SilKitMsgTraitVersion , SilKitMsgTraitSerdesName , SilKitMsgTraitForbidSelfDelivery + , SilKitMsgTraitIsSynchronizationPoint { }; @@ -110,6 +122,16 @@ struct SilKitMsgTraits } \ } +#define DefineSilKitMsgTrait_IsSynchronizationPoint(Namespace, MsgName) \ + template <> \ + struct SilKitMsgTraitIsSynchronizationPoint \ + { \ + static constexpr bool IsSynchronizationPoint() \ + { \ + return true; \ + } \ + } + DefineSilKitMsgTrait_TypeName(SilKit::Services::Logging, LogMsg); DefineSilKitMsgTrait_TypeName(VSilKit, MetricsUpdate); DefineSilKitMsgTrait_TypeName(SilKit::Services::Orchestration, SystemCommand); @@ -164,5 +186,9 @@ DefineSilKitMsgTrait_EnforceSelfDelivery(SilKit::Services::Lin, LinSendFrameHead // Messages with forbidden self delivery DefineSilKitMsgTrait_ForbidSelfDelivery(SilKit::Services::Orchestration, SystemCommand); +// Messages which are Synchronization Points +DefineSilKitMsgTrait_IsSynchronizationPoint(SilKit::Services::Flexray, FlexrayCycleStartEvent); +DefineSilKitMsgTrait_IsSynchronizationPoint(SilKit::Services::PubSub, WireDataMessageEvent); //for testing + } // namespace Core } // namespace SilKit diff --git a/SilKit/source/core/mock/participant/MockParticipant.hpp b/SilKit/source/core/mock/participant/MockParticipant.hpp index 611a852e0..b962f6f7e 100644 --- a/SilKit/source/core/mock/participant/MockParticipant.hpp +++ b/SilKit/source/core/mock/participant/MockParticipant.hpp @@ -242,7 +242,7 @@ class DummyMetricsManager : public IMetricsManager std::unordered_map _attributes; }; -class DummyParticipant : public IParticipantInternal +class DummyParticipant: public IParticipantInternal { public: DummyParticipant() @@ -250,6 +250,7 @@ class DummyParticipant : public IParticipantInternal ON_CALL(mockLifecycleService, GetTimeSyncService).WillByDefault(testing::Return(&mockTimeSyncService)); ON_CALL(mockLifecycleService, CreateTimeSyncService).WillByDefault(testing::Return(&mockTimeSyncService)); ON_CALL(logger, GetLogLevel()).WillByDefault(testing::Return(Services::Logging::Level::Debug)); + ON_CALL(*this, GetConfiguration()).WillByDefault(testing::ReturnRef(_participantConfiguration)); } auto CreateCanController(const std::string& /*canonicalName*/, @@ -720,6 +721,8 @@ class DummyParticipant : public IParticipantInternal return nullptr; } + MOCK_METHOD(const Config::ParticipantConfiguration&, GetConfiguration, (), (override)); + const std::string _name = "MockParticipant"; const std::string _registryUri = "silkit://mock.participant.silkit:0"; testing::NiceMock logger; @@ -733,6 +736,8 @@ class DummyParticipant : public IParticipantInternal MockParticipantReplies mockParticipantReplies; DummyNetworkSimulator mockNetworkSimulator; DummyMetricsManager mockMetricsManager; + Config::ParticipantConfiguration _participantConfiguration; + }; // ================================================================================ diff --git a/SilKit/source/core/participant/Participant.hpp b/SilKit/source/core/participant/Participant.hpp index 8f864b597..3d7528e9a 100644 --- a/SilKit/source/core/participant/Participant.hpp +++ b/SilKit/source/core/participant/Participant.hpp @@ -81,7 +81,7 @@ namespace SilKit { namespace Core { template -class Participant final : public IParticipantInternal +class Participant final: public IParticipantInternal { public: // ---------------------------------------- @@ -440,6 +440,11 @@ class Participant final : public IParticipantInternal auto MakeTimerThread() -> std::unique_ptr; + auto GetConfiguration() -> const Config::ParticipantConfiguration& override; + + template + void HandleSynchronizationPoint(); + private: // ---------------------------------------- // private members diff --git a/SilKit/source/core/participant/Participant_impl.hpp b/SilKit/source/core/participant/Participant_impl.hpp index 0010dcc57..2f22a8132 100644 --- a/SilKit/source/core/participant/Participant_impl.hpp +++ b/SilKit/source/core/participant/Participant_impl.hpp @@ -53,6 +53,7 @@ #include "Uuid.hpp" #include "Assert.hpp" #include "ExecutionEnvironment.hpp" +#include "traits/SilKitMsgTraits.hpp" #include "fmt/ranges.h" @@ -1323,14 +1324,35 @@ void Participant::SendMsg(const IServiceEndpoint* from, SendMsgImpl(from, std::move(msg)); } +template +template +void Participant::HandleSynchronizationPoint() +{ + if constexpr (SilKitMsgTraits>::IsSynchronizationPoint()) + { + if (auto* lifecycle = static_cast(GetLifecycleService()); lifecycle) + { + if (auto* timesync = static_cast(lifecycle->GetTimeSyncService()); + timesync) + { + if(_participantConfig.enableSynchronizationPoints) + { + timesync->TriggerSynchronization(); + } + } + } + } + +} + template template void Participant::SendMsgImpl(const IServiceEndpoint* from, SilKitMessageT&& msg) { TraceTx(GetLoggerInternal(), from, msg); _connection.SendMsg(from, std::forward(msg)); + HandleSynchronizationPoint(); } - // Targeted messaging template void Participant::SendMsg(const IServiceEndpoint* from, const std::string& targetParticipantName, @@ -1633,6 +1655,7 @@ void Participant::SendMsgImpl(const IServiceEndpoint* from, c { TraceTx(GetLoggerInternal(), from, targetParticipantName, msg); _connection.SendMsg(from, targetParticipantName, std::forward(msg)); + HandleSynchronizationPoint(); } @@ -2024,6 +2047,11 @@ auto Participant::MakeTimerThread() -> std::unique_ptrSubmitUpdates(); }); }); } +template +auto Participant::GetConfiguration() -> const Config::ParticipantConfiguration& +{ + return _participantConfig; +} } // namespace Core diff --git a/SilKit/source/core/participant/Test_Participant.cpp b/SilKit/source/core/participant/Test_Participant.cpp index 6f329d08c..28aa3cf0d 100644 --- a/SilKit/source/core/participant/Test_Participant.cpp +++ b/SilKit/source/core/participant/Test_Participant.cpp @@ -8,6 +8,20 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "WireDataMessages.hpp" +#include "WireCanMessages.hpp" +#include "WireEthernetMessages.hpp" +#include "WireLinMessages.hpp" +#include "WireFlexrayMessages.hpp" +#include "WireRpcMessages.hpp" +#include "LoggingDatatypesInternal.hpp" +#include "OrchestrationDatatypes.hpp" +#include "ServiceDatatypes.hpp" +#include "RequestReplyDatatypes.hpp" +#include "MetricsDatatypes.hpp" + +#include "traits/SilKitMsgTraits.hpp" + #include "NullConnectionParticipant.hpp" #include "CanController.hpp" #include "ConfigurationTestUtils.hpp" @@ -24,6 +38,13 @@ class Test_Participant : public testing::Test Test_Participant() {} }; +TEST(Test_Traits, ensure_traits) +{ + EXPECT_EQ(SilKitMsgTraits::TypeName(), + std::string{"SilKit::Services::PubSub::WireDataMessageEvent"}); + EXPECT_TRUE(SilKitMsgTraits::IsSynchronizationPoint()); +} + TEST_F(Test_Participant, throw_on_empty_participant_name) { EXPECT_THROW(CreateNullConnectionParticipantImpl(SilKit::Config::MakeEmptyParticipantConfigurationImpl(), ""), diff --git a/SilKit/source/core/vasio/VAsioConnection.hpp b/SilKit/source/core/vasio/VAsioConnection.hpp index 3dda909df..409074b4a 100644 --- a/SilKit/source/core/vasio/VAsioConnection.hpp +++ b/SilKit/source/core/vasio/VAsioConnection.hpp @@ -397,6 +397,14 @@ class VAsioConnection { const auto& key = from->GetServiceDescriptor().GetNetworkName(); + if constexpr (std::is_same_v>) + { + if(msg.duration == std::chrono::nanoseconds{0}) + { + (void)msg; //DEBUG break point here + } + } + auto& linkMap = std::get>>(_serviceToLinkMap); if (linkMap.count(key) < 1) { diff --git a/SilKit/source/services/orchestration/TimeConfiguration.cpp b/SilKit/source/services/orchestration/TimeConfiguration.cpp index 98acf56c3..cc1bd7ab5 100644 --- a/SilKit/source/services/orchestration/TimeConfiguration.cpp +++ b/SilKit/source/services/orchestration/TimeConfiguration.cpp @@ -77,7 +77,7 @@ void TimeConfiguration::OnReceiveNextSimStep(const std::string& participantName, { Logging::Error( _logger, - "Chonology error: Received NextSimTask from participant \'{}\' with lower timePoint {} than last " + "Chronology error: Received NextSimTask from participant '{}' with lower timePoint {} than last " "known timePoint {}", participantName, nextStep.timePoint.count(), itOtherNextTask->second.timePoint.count()); } diff --git a/SilKit/source/services/orchestration/TimeSyncService.cpp b/SilKit/source/services/orchestration/TimeSyncService.cpp index d21b9888e..d6ccb04fe 100644 --- a/SilKit/source/services/orchestration/TimeSyncService.cpp +++ b/SilKit/source/services/orchestration/TimeSyncService.cpp @@ -70,9 +70,10 @@ struct SynchronizedPolicy : public ITimeSyncPolicy public: SynchronizedPolicy(TimeSyncService& controller, Core::IParticipantInternal* participant, TimeConfiguration* configuration) - : _controller(controller) - , _participant(participant) - , _configuration(configuration) + : _controller{controller} + , _participant{participant} + , _configuration{configuration} + , _enableSynchronizationPoints{participant->GetConfiguration().enableSynchronizationPoints} { } @@ -112,6 +113,15 @@ struct SynchronizedPolicy : public ITimeSyncPolicy void ReceiveNextSimTask(const Core::IServiceEndpoint* from, const NextSimTask& task) override { + if (_enableSynchronizationPoints && task == ZeroSimTask) + { + // zero time step requested for SynchronzationPoint + auto currentStep = _configuration->CurrentSimStep(); + _controller.ExecuteSimStep(currentStep.timePoint, 0ns); + return; + } + + // normal operation _configuration->OnReceiveNextSimStep(from->GetServiceDescriptor().GetParticipantName(), task); switch (_controller.State()) @@ -282,7 +292,8 @@ struct SynchronizedPolicy : public ITimeSyncPolicy std::chrono::nanoseconds _lastSentNextSimTask{-1ns}; Core::IParticipantInternal* _participant; TimeConfiguration* _configuration; - bool _hopOnEvaluated = false; + bool _hopOnEvaluated{false}; + bool _enableSynchronizationPoints{false}; }; TimeSyncService::TimeSyncService(Core::IParticipantInternal* participant, ITimeProvider* timeProvider, diff --git a/SilKit/source/services/orchestration/TimeSyncService.hpp b/SilKit/source/services/orchestration/TimeSyncService.hpp index 9cf516c08..f712ae84a 100644 --- a/SilKit/source/services/orchestration/TimeSyncService.hpp +++ b/SilKit/source/services/orchestration/TimeSyncService.hpp @@ -99,6 +99,17 @@ class TimeSyncService void RemoveOtherSimulationStepsCompletedHandler(HandlerId handlerId); void InvokeOtherSimulationStepsCompletedHandlers(); + + // synchronization point design proposal + void TriggerSynchronization() + { + //TODO check that no async timestep is happening? + constexpr NextSimTask zeroStep{0ns, 0ns}; + static_assert(zeroStep.duration == 0ns); + static_assert(zeroStep.timePoint == 0ns); + _participant->SendMsg(this, zeroStep); + } + private: // ---------------------------------------- // private methods