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 < fastdds/utils/IPLocator.hpp>
@@ -77,13 +78,7 @@ void TCPChannelResourceBasic::connect(
7778 asio::async_connect (
7879 *socket_,
7980 endpoints,
80- [this , channel_weak_ptr](std::error_code ec
81- #if ASIO_VERSION >= 101200
82- , ip::tcp::endpoint
83- #else
84- , ip::tcp::resolver::iterator
85- #endif // if ASIO_VERSION >= 101200
86- )
81+ [this , channel_weak_ptr](std::error_code ec, ip::tcp::endpoint)
8782 {
8883 if (!channel_weak_ptr.expired ())
8984 {
@@ -101,25 +96,22 @@ void TCPChannelResourceBasic::connect(
10196
10297void TCPChannelResourceBasic::disconnect ()
10398{
104- if (eConnecting < change_status (eConnectionStatus::eDisconnected) && alive ())
99+ // Go to disconnecting state to protect from concurrent connects and disconnects
100+ auto prev_status = change_status (eConnectionStatus::eDisconnecting);
101+ if (eConnecting < prev_status && alive ())
105102 {
106- std::lock_guard<std::mutex> read_lock (read_mutex_);
107- auto socket = socket_ ;
103+ // Shutdown the socket to abort any ongoing read and write operations
104+ shutdown (asio::ip::tcp:: socket::shutdown_both) ;
108105
109- std::error_code ec ;
110- socket-> shutdown (asio::ip::tcp::socket::shutdown_both, ec);
106+ cancel () ;
107+ close (); // Blocks until all read and write operations have finished
111108
112- asio::post (context_, [&, socket]()
113- {
114- try
115- {
116- socket->cancel ();
117- socket->close ();
118- }
119- catch (std::exception&)
120- {
121- }
122- });
109+ // Change to disconnected state as the last step
110+ change_status (eConnectionStatus::eDisconnected);
111+ }
112+ else if (eConnectionStatus::eDisconnecting != prev_status || !alive ())
113+ {
114+ change_status (eConnectionStatus::eDisconnected);
123115 }
124116}
125117
@@ -128,10 +120,9 @@ uint32_t TCPChannelResourceBasic::read(
128120 std::size_t size,
129121 asio::error_code& ec)
130122{
131- std::unique_lock<std::mutex> read_lock (read_mutex_);
132-
133- if (eConnecting < connection_status_)
123+ if (connected ())
134124 {
125+ std::unique_lock<std::mutex> read_lock (read_mutex_);
135126 return static_cast <uint32_t >(asio::read (*socket_, asio::buffer (buffer, size), transfer_exactly (size), ec));
136127 }
137128
@@ -147,7 +138,7 @@ size_t TCPChannelResourceBasic::send(
147138{
148139 size_t bytes_sent = 0 ;
149140
150- if (eConnecting < connection_status_ )
141+ if (connected () )
151142 {
152143 std::lock_guard<std::mutex> send_guard (send_mutex_);
153144
@@ -195,23 +186,32 @@ asio::ip::tcp::endpoint TCPChannelResourceBasic::local_endpoint(
195186void TCPChannelResourceBasic::set_options (
196187 const TCPTransportDescriptor* options)
197188{
198- TCPChannelResource:: set_socket_options (*socket_, options);
189+ set_socket_options (*socket_, options);
199190}
200191
201192void TCPChannelResourceBasic::cancel ()
202193{
203- socket_->cancel ();
194+ std::error_code ec;
195+ socket_->cancel (ec); // thread safe with respect to asio's read and write methods
204196}
205197
206198void TCPChannelResourceBasic::close ()
207199{
208- socket_->close ();
200+ // Wait for read and write operations to finish before closing the socket (otherwise not thread safe)
201+ // NOTE: shutdown should have been called before closing to abort any ongoing operation
202+ std::unique_lock<std::mutex> send_lk (send_mutex_, std::defer_lock);
203+ std::unique_lock<std::mutex> read_lk (read_mutex_, std::defer_lock);
204+ std::lock (send_lk, read_lk); // Pre C++17 alternative to std::scoped_lock
205+
206+ std::error_code ec;
207+ socket_->close (ec);
209208}
210209
211210void TCPChannelResourceBasic::shutdown (
212211 asio::socket_base::shutdown_type what)
213212{
214- socket_->shutdown (what);
213+ std::error_code ec;
214+ socket_->shutdown (what, ec); // thread safe with respect to asio's read and write methods
215215}
216216
217217} // namespace rtps
0 commit comments