PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_sanitizerConcurrency.cpp
Go to the documentation of this file.
1
8
9#include <gtest/gtest.h>
10
11#include <atomic>
12#include <chrono>
13#include <cstdint>
14#include <memory>
15#include <thread>
16#include <vector>
17
19#include "profinet/cyclic.h"
20#include "profinet/rt.h"
21
22using namespace profinet;
23using namespace profinet::cyclic;
24
25namespace
26{
27
28constexpr MacAddress kControllerMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
29constexpr MacAddress kDeviceMac{0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
30constexpr std::uint16_t kInputFrameId = 0xC001;
31constexpr std::uint16_t kOutputFrameId = 0xC000;
32
37rt::IOCRConfig MakeConfig(std::uint16_t frameId, std::uint16_t type)
38{
39 rt::IOCRConfig config;
40 config.typeIOCR = type;
41 config.frameId = frameId;
42 config.sendClockFactor = 32U;
43 config.reductionRatio = 8U;
44 config.watchdogFactor = 3U;
45 config.dataLength = 40U;
46 config.objects.push_back(rt::IODataObject{
47 .slot = 1,
48 .subslot = 1,
49 .frameOffset = 0,
50 .dataLength = 4,
51 .offsetIOPS = 4,
52 .offsetIOCS = 5});
53 return config;
54}
55
58std::unique_ptr<CyclicController> MakeController()
59{
60 asio::io_context ioContext;
61
62 return std::make_unique<CyclicController>(
63 ioContext, "eth0", kControllerMac, kDeviceMac,
67}
68
69} // namespace
70
75TEST(SanitizerConcurrencyTest, ConcurrentStatisticsSnapshotIsRaceFree)
76{
77 auto tx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
78 auto rx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
79
80 auto controller = MakeController();
81
82 controller->Start(std::move(tx), std::move(rx));
83
84 std::atomic<bool> stop{false};
85 std::atomic<std::uint64_t> snapshots{0U};
86
87 std::thread reader([&]()
88 {
89 while (!stop.load(std::memory_order_acquire))
90 {
91 const auto snapshot = controller->StatsSnapshot();
92 static_cast<void>(snapshot.framesSent);
93 static_cast<void>(snapshot.framesReceived);
94 static_cast<void>(snapshot.framesMissed);
95 static_cast<void>(snapshot.cycleCount);
96 static_cast<void>(snapshot.cycleTimeSumUs);
97 static_cast<void>(snapshot.AvgCycleTimeUs());
98 snapshots.fetch_add(1U, std::memory_order_relaxed);
99 }
100 });
101
102 std::this_thread::sleep_for(std::chrono::milliseconds(20));
103
104 stop.store(true, std::memory_order_release);
105 reader.join();
106 controller->Stop();
107
108 EXPECT_GT(snapshots.load(std::memory_order_relaxed), 0U);
109}
110
114TEST(SanitizerConcurrencyTest, ConcurrentOutputDataWritersAreRaceFree)
115{
116 auto controller = MakeController();
117
118 constexpr std::size_t kWriterCount = 4U;
119 constexpr std::size_t kIterations = 5000U;
120 std::atomic<bool> failed{false};
121
122 std::vector<std::thread> writers;
123 writers.reserve(kWriterCount);
124
125 for (std::size_t writerIndex = 0U; writerIndex < kWriterCount; ++writerIndex)
126 {
127 writers.emplace_back([&, writerIndex]()
128 {
129 for (std::size_t iteration = 0U; iteration < kIterations; ++iteration)
130 {
131 try
132 {
133 const std::size_t length = (iteration % 16U) + 1U;
134 const auto value = static_cast<std::uint8_t>(writerIndex + iteration);
135 controller->SetOutputData(1, 1, Bytes(length, value));
136 }
137 catch (...)
138 {
139 failed.store(true, std::memory_order_release);
140 return;
141 }
142 }
143 });
144 }
145
146 for (auto& writer : writers)
147 {
148 writer.join();
149 }
150
151 EXPECT_FALSE(failed.load(std::memory_order_acquire));
152}
153
156TEST(SanitizerConcurrencyTest, RepeatedStartStopJoinsWorkers)
157{
158 auto controller = MakeController();
159
160 for (std::size_t iteration = 0U; iteration < 50U; ++iteration)
161 {
162 auto tx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
163 auto rx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
164
165 controller->Start(std::move(tx), std::move(rx));
166 EXPECT_TRUE(controller->IsRunning());
167
168 std::this_thread::sleep_for(std::chrono::milliseconds(2));
169
170 controller->Stop();
171 EXPECT_FALSE(controller->IsRunning());
172 EXPECT_EQ(controller->State(), CyclicState::Stopped);
173 }
174}
175
179TEST(SanitizerConcurrencyTest, DestructionAfterWorkerActivityIsClean)
180{
181 for (std::size_t iteration = 0U; iteration < 25U; ++iteration)
182 {
183 auto tx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
184 auto rx = std::make_unique<testing::NiceMock<MockRawEthernetSocket>>();
185
186 auto controller = MakeController();
187 controller->Start(std::move(tx), std::move(rx));
188 std::this_thread::sleep_for(std::chrono::milliseconds(2));
189 controller->Stop();
190 }
191}
Declares the PROFINET RTC1 cyclic IO controller and process-data exchange API.
GoogleMock implementation of IRawEthernetSocket.
constexpr int DEFAULT_MAX_CONSECUTIVE_TIMEOUTS
Default number of consecutive watchdog timeouts before transitioning to Fault.
Definition cyclic.h:157
constexpr std::uint16_t IOCR_TYPE_INPUT
IOCR direction: Device -> Controller.
Definition rt.h:66
constexpr std::uint16_t ETHERTYPE_PROFINET
EtherType used by PROFINET RT frames (0x8892).
Definition rt.h:34
constexpr std::uint16_t IOCR_TYPE_OUTPUT
IOCR direction: Controller -> Device.
Definition rt.h:68
constexpr MacAddress kControllerMac
TEST(AlarmEndpointTests, DefaultTransportIsLayer2)
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
constexpr std::uint16_t kInputFrameId
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
constexpr std::uint16_t kOutputFrameId
Declares PROFINET RT_CLASS_1 cyclic frame and IOCR data structures.
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
Definition rt.h:137
std::vector< IODataObject > objects
IO data objects carried in this IOCR's cyclic frame.
Definition rt.h:163
std::uint16_t dataLength
Total cyclic data length in bytes.
Definition rt.h:160
std::uint16_t typeIOCR
IOCR direction: 1=Input, 2=Output.
Definition rt.h:139
std::uint16_t sendClockFactor
Send clock base factor (31.25us units).
Definition rt.h:148
std::uint16_t frameId
Frame ID assigned to this IOCR.
Definition rt.h:145
std::uint16_t reductionRatio
Reduction ratio relative to the send clock.
Definition rt.h:151
std::uint16_t watchdogFactor
Watchdog factor (missed-frame tolerance before a timeout fault).
Definition rt.h:157
Single IO data object within the C_SDU: one piece of process data mapped to a slot/subslot,...
Definition rt.h:109
std::uint16_t slot
Slot number.
Definition rt.h:111
std::uint16_t frameId
Ethernet FrameID.
std::unique_ptr< MockRawEthernetSocket > tx
std::unique_ptr< MockRawEthernetSocket > rx
std::unique_ptr< cyclic::CyclicController > controller
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
std::uint16_t length
Definition test_dcp.cpp:391