I have a fairly simple use case. I would like to read from a boost socket using boost::asio::read
, but with a timeout for the read call. I.e. If nothing nothing is read from the socket in 5 seconds, the call should terminate/throw-an-error/whatever. The code without a timeout is shown below:
Json::Value Client::MakeRequest(const std::string &ip_addr, unsigned short port,
const Json::Value &request)
{
boost::asio::io_context io_context;
Json::StreamWriterBuilder writer_;
std::string serialized_req = Json::writeString(writer_, request);
tcp::socket s(io_context);
tcp::resolver resolver(io_context);
try {
s.connect({boost::asio::ip::address::from_string(ip_addr), port});
} catch(const std::exception &err) {
throw std::runtime_error(err.what());
}
boost::asio::write(s, boost::asio::buffer(serialized_req));
s.shutdown(tcp::socket::shutdown_send);
error_code ec;
char reply[2048];
// I would like to replace this with a call which times out.
size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply),
ec);
...
}
There are examples on stackoverflow stating how to accomplish this using the deprecated boost::asio::io_service
. However, my code uses io_context
instead, so this is not viable. This code, from an answer by Tom Trebicky in 2017, accomplishes my task using io_service
:
template <typename SyncReadStream, typename MutableBufferSequence>
void readWithTimeout(SyncReadStream& s, const MutableBufferSequence& buffers, const boost::asio::deadline_timer::duration_type& expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_io_service());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const boost::system::error_code& error) { timer_result.reset(error); });
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers, [&read_result] (const boost::system::error_code& error, size_t) { read_result.reset(error); });
s.get_io_service().reset();
while (s.get_io_service().run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
I have tried to convert this to using io_context
, but to no avail. Can someone provide a version which reads from a socket with timeouts using io_context
?
At present, my attempt to transfer this over to io_context
is this:
template <typename SyncReadStream, typename MutableBufferSequence>
static void ReadWithTimeout(SyncReadStream &s,
const MutableBufferSequence &buffers,
const boost::asio::deadline_timer::duration_type
&expiry_time)
{
boost::optional<boost::system::error_code> timer_result;
boost::asio::deadline_timer timer(s.get_executor().context());
timer.expires_from_now(expiry_time);
timer.async_wait([&timer_result] (const error_code& error)
{
timer_result.reset(error);
});
boost::optional<boost::system::error_code> read_result;
boost::asio::async_read(s, buffers,
[&read_result] (const error_code& error, size_t)
{
read_result.reset(error);
});
s.get_executor().template target<boost::asio::io_context>()->reset();
while (s.get_executor().template target<boost::asio::io_context>()->run_one())
{
if (read_result)
timer.cancel();
else if (timer_result)
s.cancel();
}
if (*read_result)
throw boost::system::system_error(*read_result);
}
This gives the following error:
====================[ Build | chord_and_dhash | Debug ]=========================
/opt/clion-2021.1.3/bin/cmake/linux/bin/cmake --build /home/patrick/CLionProjects/chord_and_dhash/cmake-build-debug --target chord_and_dhash -- -j 6
[ 8%] Built target gtest
[ 26%] Built target jsoncpp_lib
[ 34%] Built target gtest_main
Scanning dependencies of target chord_and_dhash
[ 39%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/abstract_chord_peer.cpp.o
[ 43%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/chord_peer.cpp.o
[ 47%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/server_test.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/chord/remote_peer.cpp.o
[ 56%] Building CXX object CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o
[ 60%] Building CXX object CMakeFiles/chord_and_dhash.dir/test/chord_test.cpp.o
In file included from /usr/include/boost/asio/basic_socket.hpp:22,
from /usr/include/boost/asio/basic_datagram_socket.hpp:20,
from /usr/include/boost/asio.hpp:24,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:15,
from /home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:1:
/usr/include/boost/asio/detail/io_object_impl.hpp: In instantiation of ‘boost::asio::detail::io_object_impl<IoObjectService, Executor>::io_object_impl(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; IoObjectService = boost::asio::detail::deadline_timer_service<boost::asio::time_traits<boost::posix_time::ptime> >; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’:
/usr/include/boost/asio/basic_deadline_timer.hpp:182:20: required from ‘boost::asio::basic_deadline_timer<Time, TimeTraits, Executor>::basic_deadline_timer(ExecutionContext&, typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type*) [with ExecutionContext = boost::asio::execution_context; Time = boost::posix_time::ptime; TimeTraits = boost::asio::time_traits<boost::posix_time::ptime>; Executor = boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >; typename std::enable_if<std::is_convertible<ExecutionContext&, boost::asio::execution_context&>::value>::type = void]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.h:45:37: required from ‘static void Client::ReadWithTimeout(SyncReadStream&, const MutableBufferSequence&, const duration_type&) [with SyncReadStream = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; MutableBufferSequence = boost::asio::mutable_buffers_1; boost::asio::basic_deadline_timer<boost::posix_time::ptime>::duration_type = boost::posix_time::time_duration]’
/home/patrick/CLionProjects/chord_and_dhash/src/networking/client.cpp:70:81: required from here
/usr/include/boost/asio/detail/io_object_impl.hpp:61:25: error: ‘class boost::asio::execution_context’ has no member named ‘get_executor’
61 | executor_(context.get_executor())
| ~~~~~~~~^~~~~~~~~~~~
gmake[3]: *** [CMakeFiles/chord_and_dhash.dir/build.make:199: CMakeFiles/chord_and_dhash.dir/src/networking/client.cpp.o] Error 1
gmake[3]: *** Waiting for unfinished jobs....
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/test/chord_test.cpp:2:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/test/../src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
In file included from /home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/database.h:17,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/abstract_chord_peer.h:42,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.h:21,
from /home/patrick/CLionProjects/chord_and_dhash/src/chord/chord_peer.cpp:1:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h: In member function ‘CSMerkleNode<DataType>::operator Json::Value() const’:
/home/patrick/CLionProjects/chord_and_dhash/src/chord/../data_structures/merkle_node.h:294:5: warning: no return statement in function returning non-void [-Wreturn-type]
294 | }
| ^
gmake[2]: *** [CMakeFiles/Makefile2:313: CMakeFiles/chord_and_dhash.dir/all] Error 2
gmake[1]: *** [CMakeFiles/Makefile2:320: CMakeFiles/chord_and_dhash.dir/rule] Error 2
gmake: *** [Makefile:183: chord_and_dhash] Error 2