Skip to content

Commit edb2448

Browse files
kitsnetLittleHuba
authored andcommitted
lib/message_passing: make sync first connection an option
GIT_ORIGIN_SPP_REV_ID: 1d2daddd4bac1f53306f56451876efec19818d3a
1 parent c43293f commit edb2448

File tree

6 files changed

+169
-19
lines changed

6 files changed

+169
-19
lines changed

score/message_passing/client_connection.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,20 @@ void ClientConnection::DoRestart() noexcept
334334
<< std::endl;
335335

336336
connect_retry_ms_ = kConnectRetryMsStart;
337-
TryConnect();
337+
if (client_config_.sync_first_connect)
338+
{
339+
TryConnect();
340+
}
341+
else
342+
{
343+
engine_->EnqueueCommand(
344+
connection_timer_,
345+
ISharedResourceEngine::TimePoint{},
346+
[this](auto) noexcept {
347+
TryConnect();
348+
},
349+
this);
350+
}
338351
}
339352

340353
void ClientConnection::TryConnect() noexcept

score/message_passing/client_connection_test.cpp

Lines changed: 149 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,22 @@ class ClientConnectionTest : public ::testing::Test
6565
engine_.reset();
6666
}
6767

68-
void CatchImmediateConnectEndpointRegistrationCommand()
68+
void CatchImmediateConnectCommand()
6969
{
7070
EXPECT_CALL(*engine_, EnqueueCommand(_, ISharedResourceEngine::TimePoint{}, _, _))
7171
.WillOnce([&](auto&, auto, ISharedResourceEngine::CommandCallback callback, auto) {
72-
// reusing the connect callback in case ow queued endpoint registration
7372
connect_command_callback_ = std::move(callback);
7473
});
7574
}
7675

76+
void CatchEndpointRegistrationCommand()
77+
{
78+
EXPECT_CALL(*engine_, EnqueueCommand(_, ISharedResourceEngine::TimePoint{}, _, _))
79+
.WillOnce([&](auto&, auto, ISharedResourceEngine::CommandCallback callback, auto) {
80+
endpoint_command_callback_ = std::move(callback);
81+
});
82+
}
83+
7784
void CatchTimedConnectCommand()
7885
{
7986
EXPECT_CALL(*engine_, EnqueueCommand(_, Gt(ISharedResourceEngine::Clock::now()), _, _))
@@ -126,6 +133,17 @@ class ClientConnectionTest : public ::testing::Test
126133
on_callback_thread_ = false;
127134
}
128135

136+
void InvokeEndpointRegistrationCommand()
137+
{
138+
on_callback_thread_ = true;
139+
{
140+
auto callback = std::move(endpoint_command_callback_);
141+
callback(ISharedResourceEngine::Clock::now());
142+
// the destructor for callback capture, if any, is called now
143+
}
144+
on_callback_thread_ = false;
145+
}
146+
129147
void InvokeDisconnectCommand()
130148
{
131149
on_callback_thread_ = true;
@@ -200,13 +218,26 @@ class ClientConnectionTest : public ::testing::Test
200218
IClientConnection::StateCallback state_callback = {},
201219
IClientConnection::NotifyCallback notify_callback = {})
202220
{
203-
AtTryOpenCall_Return(kValidFd);
204-
CatchImmediateConnectEndpointRegistrationCommand();
221+
if (client_config_.sync_first_connect)
222+
{
223+
AtTryOpenCall_Return(kValidFd);
224+
CatchEndpointRegistrationCommand();
205225

206-
connection.Start(std::move(state_callback), std::move(notify_callback));
226+
connection.Start(std::move(state_callback), std::move(notify_callback));
207227

208-
CatchPosixEndpointRegistration();
209-
InvokeConnectCommand();
228+
CatchPosixEndpointRegistration();
229+
InvokeEndpointRegistrationCommand();
230+
}
231+
else
232+
{
233+
CatchImmediateConnectCommand();
234+
connection.Start(std::move(state_callback), std::move(notify_callback));
235+
EXPECT_EQ(connection.GetState(), State::kStarting);
236+
237+
AtTryOpenCall_Return(kValidFd);
238+
CatchPosixEndpointRegistration();
239+
InvokeConnectCommand();
240+
}
210241

211242
EXPECT_EQ(connection.GetState(), State::kReady);
212243
EXPECT_EQ(posix_endpoint_->fd, kValidFd);
@@ -216,12 +247,20 @@ class ClientConnectionTest : public ::testing::Test
216247

217248
void StopConnectionInProgress(detail::ClientConnection& connection)
218249
{
219-
CatchDisconnectCommand();
220-
connection.Stop();
221-
EXPECT_EQ(connection.GetState(), State::kStopping);
222-
EXPECT_EQ(connection.GetStopReason(), StopReason::kUserRequested);
250+
if (client_config_.sync_first_connect)
251+
{
252+
// for the immediate stop on the user thread
253+
CatchDisconnectCommand();
254+
connection.Stop();
255+
EXPECT_EQ(connection.GetState(), State::kStopping);
256+
EXPECT_EQ(connection.GetStopReason(), StopReason::kUserRequested);
223257

224-
InvokeDisconnectCommand();
258+
InvokeDisconnectCommand();
259+
}
260+
else
261+
{
262+
connection.Stop();
263+
}
225264
EXPECT_EQ(connection.GetState(), State::kStopped);
226265
EXPECT_EQ(connection.GetStopReason(), StopReason::kUserRequested);
227266
}
@@ -259,8 +298,9 @@ class ClientConnectionTest : public ::testing::Test
259298
std::shared_ptr<StrictMock<SharedResourceEngineMock>> engine_{};
260299
const std::string service_identifier_{"test_identifier"};
261300
const ServiceProtocolConfig protocol_config_{service_identifier_, kMaxSendSize, kMaxReplySize, kMaxNotifySize};
262-
IClientFactory::ClientConfig client_config_{0, 1, false, false};
301+
IClientFactory::ClientConfig client_config_{0, 1, false, false, false};
263302
ISharedResourceEngine::CommandCallback connect_command_callback_{};
303+
ISharedResourceEngine::CommandCallback endpoint_command_callback_{};
264304
ISharedResourceEngine::CommandCallback disconnect_command_callback_{};
265305
ISharedResourceEngine::CommandCallback send_queue_command_callback_{};
266306
ISharedResourceEngine::PosixEndpointEntry* posix_endpoint_{};
@@ -287,6 +327,27 @@ TEST_F(ClientConnectionTest, TryingToConnectOnceStoppingWhileConnecting)
287327
{
288328
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
289329

330+
CatchImmediateConnectCommand();
331+
connection.Start(IClientConnection::StateCallback(), IClientConnection::NotifyCallback());
332+
EXPECT_EQ(connection.GetState(), State::kStarting);
333+
334+
EXPECT_CALL(*engine_, TryOpenClientConnection(std::string_view{service_identifier_}))
335+
.WillOnce([this, &connection](auto&&) {
336+
StopConnectionInProgress(connection);
337+
return score::cpp::make_unexpected(score::os::Error::createFromErrno(EIO)); // this error code would be ignored
338+
});
339+
ExpectCleanUpOwner(connection);
340+
InvokeConnectCommand();
341+
342+
EXPECT_EQ(connection.GetState(), State::kStopped);
343+
EXPECT_EQ(connection.GetStopReason(), StopReason::kUserRequested);
344+
}
345+
346+
TEST_F(ClientConnectionTest, TryingToSyncConnectOnceStoppingWhileConnecting)
347+
{
348+
client_config_.sync_first_connect = true;
349+
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
350+
290351
EXPECT_CALL(*engine_, TryOpenClientConnection(std::string_view{service_identifier_}))
291352
.WillOnce([this, &connection](auto&&) {
292353
StopConnectionInProgress(connection);
@@ -301,6 +362,30 @@ TEST_F(ClientConnectionTest, TryingToConnectOnceStoppingWhileConnecting)
301362

302363
TEST_F(ClientConnectionTest, TryingToConnectOnceStoppingOnHardError)
303364
{
365+
const auto error_list_to_test = {std::make_pair(EACCES, StopReason::kPermission),
366+
std::make_pair(EPIPE, StopReason::kIoError)};
367+
368+
for (auto entry : error_list_to_test)
369+
{
370+
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
371+
372+
CatchImmediateConnectCommand();
373+
connection.Start(IClientConnection::StateCallback(), IClientConnection::NotifyCallback());
374+
EXPECT_EQ(connection.GetState(), State::kStarting);
375+
376+
AtTryOpenCall_Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(entry.first)));
377+
ExpectCleanUpOwner(connection);
378+
InvokeConnectCommand();
379+
380+
EXPECT_EQ(connection.GetState(), State::kStopped);
381+
EXPECT_EQ(connection.GetStopReason(), entry.second);
382+
}
383+
}
384+
385+
TEST_F(ClientConnectionTest, TryingToSyncConnectOnceStoppingOnHardError)
386+
{
387+
client_config_.sync_first_connect = true;
388+
304389
const auto error_list_to_test = {std::make_pair(EACCES, StopReason::kPermission),
305390
std::make_pair(EPIPE, StopReason::kIoError)};
306391

@@ -323,6 +408,38 @@ TEST_F(ClientConnectionTest, TryingToConnectMultipleTimesStoppingOnPermissionErr
323408

324409
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
325410

411+
for (auto entry : error_list_to_test)
412+
{
413+
if (entry == *error_list_to_test.begin())
414+
{
415+
CatchImmediateConnectCommand();
416+
connection.Start(IClientConnection::StateCallback(), IClientConnection::NotifyCallback());
417+
}
418+
else
419+
{
420+
AtTryOpenCall_Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(entry)));
421+
CatchTimedConnectCommand();
422+
InvokeConnectCommand();
423+
}
424+
EXPECT_EQ(connection.GetState(), State::kStarting);
425+
}
426+
427+
AtTryOpenCall_Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(EACCES)));
428+
ExpectCleanUpOwner(connection);
429+
InvokeConnectCommand();
430+
431+
EXPECT_EQ(connection.GetState(), State::kStopped);
432+
EXPECT_EQ(connection.GetStopReason(), StopReason::kPermission);
433+
}
434+
435+
TEST_F(ClientConnectionTest, TryingToSyncConnectMultipleTimesStoppingOnPermissionError)
436+
{
437+
client_config_.sync_first_connect = true;
438+
439+
const auto error_list_to_test = {EAGAIN, ECONNREFUSED, ENOENT};
440+
441+
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
442+
326443
for (auto entry : error_list_to_test)
327444
{
328445
AtTryOpenCall_Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(entry)));
@@ -350,8 +467,7 @@ TEST_F(ClientConnectionTest, TryingToConnectMultipleTimesFinallyConnectingAndImp
350467
{
351468
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
352469

353-
AtTryOpenCall_Return(score::cpp::make_unexpected(score::os::Error::createFromErrno(ENOENT)));
354-
CatchTimedConnectCommand();
470+
CatchImmediateConnectCommand();
355471
connection.Start(IClientConnection::StateCallback(), IClientConnection::NotifyCallback());
356472
EXPECT_EQ(connection.GetState(), State::kStarting);
357473

@@ -383,6 +499,15 @@ TEST_F(ClientConnectionTest, SuccessfullyConnectingAtFirstAttemptThenExplicitlyS
383499
StopCurrentConnection(connection);
384500
}
385501

502+
TEST_F(ClientConnectionTest, SuccessfullySyncConnectingAtFirstAttemptThenExplicitlyStopping)
503+
{
504+
client_config_.sync_first_connect = true;
505+
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
506+
MakeSuccessfulConnection(connection);
507+
508+
StopCurrentConnection(connection);
509+
}
510+
386511
TEST_F(ClientConnectionTest, SuccessfullyConnectingAtFirstAttemptThenExplicitlyStoppingAsIfFromCallback)
387512
{
388513
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
@@ -391,6 +516,15 @@ TEST_F(ClientConnectionTest, SuccessfullyConnectingAtFirstAttemptThenExplicitlyS
391516
StopCurrentConnectionAsIfInCallback(connection);
392517
}
393518

519+
TEST_F(ClientConnectionTest, SuccessfullySyncConnectingAtFirstAttemptThenExplicitlyStoppingAsIfFromCallback)
520+
{
521+
client_config_.sync_first_connect = true;
522+
detail::ClientConnection connection(engine_, protocol_config_, client_config_);
523+
MakeSuccessfulConnection(connection);
524+
525+
StopCurrentConnectionAsIfInCallback(connection);
526+
}
527+
394528
TEST_F(ClientConnectionTest, SuccessfullyConnectingAtFirstAttemptThenFirstReadDisconnected)
395529
{
396530
detail::ClientConnection connection(engine_, protocol_config_, client_config_);

score/message_passing/i_client_factory.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class IClientFactory
5252
// coverity[autosar_cpp14_a9_6_1_violation : FALSE]
5353
bool truly_async; ///< true if Send and SendWithCallback calls always use background thread
5454
///< for IPC (requires nonzero max_queued_sends)
55+
// coverity[autosar_cpp14_a9_6_1_violation : FALSE]
56+
bool sync_first_connect; ///< true if the first connection attempt uses the thread on which Start() is called
57+
///< (can lead to deadlocks if the connection is established from within a callback)
5558
};
5659

5760
/// \brief Creates an implementation instance of IClientConnection.

score/message_passing/qnx_dispatch_server_to_client_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class ServerToClientQnxFixture : public ::testing::Test, public testing::WithPar
5050
test_prefix += std::to_string(::getpid()) + "_";
5151
service_identifier_ = test_prefix + "1";
5252
protocol_config_ = ServiceProtocolConfig{service_identifier_, 6U, 6U, 6U};
53-
client_config_ = IClientFactory::ClientConfig{1U, 1U, false, true};
53+
client_config_ = IClientFactory::ClientConfig{1U, 1U, false, true, false};
5454
server_config_ = IServerFactory::ServerConfig{0U, 0U, 1U};
5555

5656
server_connections_started_ = 0U;

score/message_passing/unix_domain_server_to_client_test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class ServerToClientTestFixtureUnix : public ::testing::Test, public testing::Wi
5050
test_prefix += std::to_string(::getpid()) + "_";
5151
service_identifier_ = test_prefix + "1";
5252
protocol_config_ = ServiceProtocolConfig{service_identifier_, 1024, 1024, 1024};
53-
client_config_ = IClientFactory::ClientConfig{1, 1, false, true};
53+
client_config_ = IClientFactory::ClientConfig{1, 1, false, true, false};
5454

5555
server_connections_started_ = 0;
5656
server_connections_finished_ = 0;

score/mw/com/impl/bindings/lola/messaging/message_passing_client_cache.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ std::shared_ptr<score::message_passing::IClientConnection> MessagePassingClientC
7777

7878
const bool fully_async = asil_level_ == ClientQualityType::kASIL_QMfromB;
7979
const score::message_passing::ServiceProtocolConfig protocol_config{service_identifier, kMaxSendSize, 0U, 0U};
80-
const score::message_passing::IClientFactory::ClientConfig client_config{0U, 20U, false, fully_async};
80+
const score::message_passing::IClientFactory::ClientConfig client_config{0U, 20U, false, fully_async, true};
8181

8282
auto new_sender_unique_p = client_factory_.Create(protocol_config, client_config);
8383

0 commit comments

Comments
 (0)