1414
1515#include < rtps/transport/TCPChannelResourceBasic.h>
1616
17- #include < future>
1817#include < array>
18+ #include < future>
19+ #include < mutex>
1920
2021#include < asio.hpp>
2122#include < fastrtps/utils/IPLocator.h>
@@ -79,13 +80,7 @@ void TCPChannelResourceBasic::connect(
7980 asio::async_connect (
8081 *socket_,
8182 endpoints,
82- [this , channel_weak_ptr](std::error_code ec
83- #if ASIO_VERSION >= 101200
84- , ip::tcp::endpoint
85- #else
86- , ip::tcp::resolver::iterator
87- #endif // if ASIO_VERSION >= 101200
88- )
83+ [this , channel_weak_ptr](std::error_code ec, ip::tcp::endpoint)
8984 {
9085 if (!channel_weak_ptr.expired ())
9186 {
@@ -103,25 +98,22 @@ void TCPChannelResourceBasic::connect(
10398
10499void TCPChannelResourceBasic::disconnect ()
105100{
106- if (eConnecting < change_status (eConnectionStatus::eDisconnected) && alive ())
101+ // Go to disconnecting state to protect from concurrent connects and disconnects
102+ auto prev_status = change_status (eConnectionStatus::eDisconnecting);
103+ if (eConnecting < prev_status && alive ())
107104 {
108- std::lock_guard<std::mutex> read_lock (read_mutex_);
109- auto socket = socket_ ;
105+ // Shutdown the socket to abort any ongoing read and write operations
106+ shutdown (asio::ip::tcp:: socket::shutdown_both) ;
110107
111- std::error_code ec ;
112- socket-> shutdown (asio::ip::tcp::socket::shutdown_both, ec);
108+ cancel () ;
109+ close (); // Blocks until all read and write operations have finished
113110
114- asio::post (context_, [&, socket]()
115- {
116- try
117- {
118- socket->cancel ();
119- socket->close ();
120- }
121- catch (std::exception&)
122- {
123- }
124- });
111+ // Change to disconnected state as the last step
112+ change_status (eConnectionStatus::eDisconnected);
113+ }
114+ else if (eConnectionStatus::eDisconnecting != prev_status || !alive ())
115+ {
116+ change_status (eConnectionStatus::eDisconnected);
125117 }
126118}
127119
@@ -130,10 +122,9 @@ uint32_t TCPChannelResourceBasic::read(
130122 std::size_t size,
131123 asio::error_code& ec)
132124{
133- std::unique_lock<std::mutex> read_lock (read_mutex_);
134-
135- if (eConnecting < connection_status_)
125+ if (connected ())
136126 {
127+ std::unique_lock<std::mutex> read_lock (read_mutex_);
137128 return static_cast <uint32_t >(asio::read (*socket_, asio::buffer (buffer, size), transfer_exactly (size), ec));
138129 }
139130
@@ -149,7 +140,7 @@ size_t TCPChannelResourceBasic::send(
149140{
150141 size_t bytes_sent = 0 ;
151142
152- if (eConnecting < connection_status_ )
143+ if (connected () )
153144 {
154145 std::lock_guard<std::mutex> send_guard (send_mutex_);
155146
@@ -200,23 +191,32 @@ asio::ip::tcp::endpoint TCPChannelResourceBasic::local_endpoint(
200191void TCPChannelResourceBasic::set_options (
201192 const TCPTransportDescriptor* options)
202193{
203- TCPChannelResource:: set_socket_options (*socket_, options);
194+ set_socket_options (*socket_, options);
204195}
205196
206197void TCPChannelResourceBasic::cancel ()
207198{
208- socket_->cancel ();
199+ std::error_code ec;
200+ socket_->cancel (ec); // thread safe with respect to asio's read and write methods
209201}
210202
211203void TCPChannelResourceBasic::close ()
212204{
213- socket_->close ();
205+ // Wait for read and write operations to finish before closing the socket (otherwise not thread safe)
206+ // NOTE: shutdown should have been called before closing to abort any ongoing operation
207+ std::unique_lock<std::mutex> send_lk (send_mutex_, std::defer_lock);
208+ std::unique_lock<std::mutex> read_lk (read_mutex_, std::defer_lock);
209+ std::lock (send_lk, read_lk); // Pre C++17 alternative to std::scoped_lock
210+
211+ std::error_code ec;
212+ socket_->close (ec);
214213}
215214
216215void TCPChannelResourceBasic::shutdown (
217216 asio::socket_base::shutdown_type what)
218217{
219- socket_->shutdown (what);
218+ std::error_code ec;
219+ socket_->shutdown (what, ec); // thread safe with respect to asio's read and write methods
220220}
221221
222222} // namespace rtps
0 commit comments