Skip to content

Commit 8f6f36b

Browse files
kai-moritzkumkarduartenfonseca
authored andcommitted
Change order of local endpoint clean-up
Summary Change the order how a connection is cleaned-up. Details When a server -> client connection is broken the endpoint needs to be cleaned up, before the corresponding client -> server connection is closed. Otherwise it can happen that the later is restored and the former tried to be used to acknowledge a subscription.
1 parent b9586de commit 8f6f36b

File tree

8 files changed

+101
-1
lines changed

8 files changed

+101
-1
lines changed

implementation/routing/src/routing_manager_client.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2870,14 +2870,17 @@ void routing_manager_client::handle_client_error(client_t _client) {
28702870
}
28712871
}
28722872

2873+
// First ensure that the connection is dropped, before enforcing a
2874+
// reconnect from the client. Otherwise a client subscribe might
2875+
// be handled by a partially cleaned-up connection
2876+
remove_local(_client, true);
28732877
// Remove the client from the local connections.
28742878
{
28752879
std::scoped_lock lock {receiver_mutex_};
28762880
if (auto endpoint = std::dynamic_pointer_cast<server_endpoint>(receiver_)) {
28772881
endpoint->disconnect_from(_client);
28782882
}
28792883
}
2880-
remove_local(_client, true);
28812884

28822885
// Request the host these services again.
28832886
if ( state_ == inner_state_type_e::ST_REGISTERED ) {

test/network_tests/fake_socket_tests/helpers/base_fake_socket_fixture.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,9 @@ void base_fake_socket_fixture::set_ignore_connections(std::string const& _app_na
105105
return socket_manager_->delay_message_processing(_from, _to, _delay);
106106
}
107107

108+
[[nodiscard]] bool base_fake_socket_fixture::block_on_close_for(
109+
std::string const& _from, std::optional<std::chrono::milliseconds> _from_block_time,
110+
std::string const& _to, std::optional<std::chrono::milliseconds> _to_block_time) {
111+
return socket_manager_->block_on_close_for(_from, _from_block_time, _to, _to_block_time);
112+
}
108113
}

test/network_tests/fake_socket_tests/helpers/base_fake_socket_fixture.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ struct base_fake_socket_fixture : ::testing::Test {
145145
[[nodiscard]] bool delay_message_processing(std::string const& _from, std::string const& _to,
146146
bool _delay);
147147

148+
/**
149+
* @see socket_manager::block_on_close_for()
150+
**/
151+
[[nodiscard]] bool block_on_close_for(std::string const& _from,
152+
std::optional<std::chrono::milliseconds> _from_block_time,
153+
std::string const& _to,
154+
std::optional<std::chrono::milliseconds> _to_block_time);
155+
148156
private:
149157
static std::shared_ptr<fake_socket_factory> factory_;
150158
std::shared_ptr<socket_manager> socket_manager_ {std::make_shared<socket_manager>()};

test/network_tests/fake_socket_tests/helpers/fake_tcp_socket_handle.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "test_logging.hpp"
1010

1111
#include <span>
12+
#include <thread>
1213
#include <numeric>
1314

1415
namespace vsomeip_v3::testing {
@@ -74,6 +75,16 @@ void fake_tcp_socket_handle::close() {
7475
if (remote) {
7576
remote->inner_close();
7677
}
78+
auto block_time = [&] {
79+
auto const lock = std::scoped_lock(mtx_);
80+
return block_on_close_time_;
81+
}();
82+
if (block_time) {
83+
TEST_LOG << "[fake-socket] delaying close procesesing for: " << socket_id_
84+
<< " by: " << block_time->count() << "ms";
85+
std::this_thread::sleep_for(*block_time);
86+
TEST_LOG << "[fake-socket] continuing close procesesing for: " << socket_id_;
87+
}
7788
}
7889

7990
void fake_tcp_socket_handle::shutdown() {
@@ -126,6 +137,12 @@ void fake_tcp_socket_handle::delay_processing(bool _delay) {
126137
update_reception();
127138
}
128139

140+
void fake_tcp_socket_handle::block_on_close_for(
141+
std::optional<std::chrono::milliseconds> _block_time) {
142+
auto const lock = std::scoped_lock(mtx_);
143+
block_on_close_time_ = _block_time;
144+
}
145+
129146
void fake_tcp_socket_handle::inner_close() {
130147
// called by the remote connected socket
131148
auto const lock = std::scoped_lock(mtx_);

test/network_tests/fake_socket_tests/helpers/fake_tcp_socket_handle.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ struct fake_tcp_socket_handle : std::enable_shared_from_this<fake_tcp_socket_han
163163
**/
164164
void delay_processing(bool _delay);
165165

166+
/**
167+
* Lets the thread calling ::close sleep for _block_time after informing
168+
* the connected socket about the closing.
169+
**/
170+
void block_on_close_for(std::optional<std::chrono::milliseconds> _block_time);
171+
166172
void set_app_name(std::string const& _name);
167173
std::string get_app_name();
168174

@@ -187,6 +193,7 @@ struct fake_tcp_socket_handle : std::enable_shared_from_this<fake_tcp_socket_han
187193
std::optional<Receptor> receptor_;
188194
boost::asio::ip::tcp::endpoint local_ep_;
189195
boost::asio::ip::tcp::endpoint remote_ep_;
196+
std::optional<std::chrono::milliseconds> block_on_close_time_;
190197
std::mutex mtx_;
191198
};
192199

test/network_tests/fake_socket_tests/helpers/socket_manager.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,4 +349,27 @@ void socket_manager::set_ignore_connections(std::string const& _app_name,
349349
}
350350
return false;
351351
}
352+
353+
[[nodiscard]] bool socket_manager::block_on_close_for(
354+
std::string const& _from, std::optional<std::chrono::milliseconds> _from_block_time,
355+
std::string const& _to, std::optional<std::chrono::milliseconds> _to_block_time) {
356+
auto const lock = std::scoped_lock(mtx_);
357+
auto const cn = connection_name(_from, _to);
358+
auto const it_connection = app_names_to_connection.find(cn);
359+
if (it_connection == app_names_to_connection.end()) {
360+
return false;
361+
}
362+
bool ret = true;
363+
if (auto from = it_connection->second.first.lock(); from) {
364+
from->block_on_close_for(_from_block_time);
365+
} else {
366+
ret = false;
367+
}
368+
if (auto to = it_connection->second.second.lock(); to) {
369+
to->block_on_close_for(_to_block_time);
370+
} else {
371+
ret = false;
372+
}
373+
return ret;
374+
}
352375
}

test/network_tests/fake_socket_tests/helpers/socket_manager.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,19 @@ class socket_manager : public std::enable_shared_from_this<socket_manager> {
114114
**/
115115
[[nodiscard]] bool delay_message_processing(std::string const& _from, std::string const& _to,
116116
bool _delay);
117+
118+
/**
119+
* searches for the _from -> _to connected sockets and demands from _from to block execution
120+
* for _from_block_time when close is invoked, equivalent for _to with _to_block_time.
121+
* @see fake_tcp_socket_handle::block_on_close_for() for further details.
122+
*
123+
* @return true, if all non nullopts could be forwarded
124+
**/
125+
[[nodiscard]] bool block_on_close_for(std::string const& _from,
126+
std::optional<std::chrono::milliseconds> _from_block_time,
127+
std::string const& _to,
128+
std::optional<std::chrono::milliseconds> _to_block_time);
129+
117130
/**
118131
* associates a fake_tcp_socket_handle to a io_context and therefore to an app_name.
119132
**/

test/network_tests/fake_socket_tests/test_connection_restoration.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,30 @@ TEST_F(test_client_helper, client_ignores_server_connection_attempt_once) {
305305
EXPECT_TRUE(subscribe_to_event());
306306
}
307307

308+
TEST_F(test_client_helper,
309+
given_server_to_client_breaksdown_when_the_connection_is_re_established_then_the_subscription_confirmed) {
310+
start_apps();
311+
send_field_message();
312+
ASSERT_TRUE(subscribe_to_field());
313+
ASSERT_TRUE(client_->message_record_.wait_for(first_expected_field_message_));
314+
client_->subscription_record_.clear();
315+
316+
// give the client a head start when cleaning the "sibling" connection, by holding back the
317+
// server execution.
318+
ASSERT_TRUE(block_on_close_for(client_name_, std::nullopt, server_name_,
319+
std::chrono::milliseconds(200)));
320+
321+
// break the server->client connection. Notice that if the blocking above is in place,
322+
// the "wrong" order of cleaning up server->client + client->server will lead to a race
323+
// condition.
324+
ASSERT_TRUE(
325+
disconnect(server_name_, boost::asio::error::timed_out, client_name_, std::nullopt));
326+
327+
EXPECT_TRUE(client_->subscription_record_.wait_for(
328+
event_subscription::successfully_subscribed_to(offered_field_),
329+
std::chrono::seconds(6)));
330+
}
331+
308332
struct test_single_connection_breakdown
309333
: test_client_helper,
310334
::testing::WithParamInterface<std::pair<std::string, std::string>> { };

0 commit comments

Comments
 (0)