Note: Not sure if this is due to Asio bug, Azmq bug or my own ignorance.
I am using an Azmq library to read off a socket, this library uses its own type of socket (azmq::socket) which I am pretty sure is a wrapper for a boost::asio::socket. The flow of execution is the following:
Timer function for reference
auto timer(std::chrono::steady_clock::duration dur) -> asio::awaitable<void> {
asio::steady_timer timer(co_await asio::this_coro::executor);
timer.expires_after(dur);
co_await timer.async_wait(asio::use_awaitable);
}
auto val = co_await(azmq::async_receive(socket_, asio::buffer(buffer), asio::use_awaitable) || timer(std::chrono::seconds{1}));
template<class CompletionToken, class MutableBufferSequence>
auto async_receive(azmq::socket &socket, MutableBufferSequence const &buffers, CompletionToken &&token) -> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::system::error_code, size_t)) {
return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, size_t)>(async_receive_initiation<MutableBufferSequence>{socket, buffers}, token);
}
template<typename MutableBufferSequence>
struct async_receive_initiation {
azmq::socket &socket;
MutableBufferSequence const &buffers;
template<typename CompletionHandler>
void operator()(CompletionHandler &&completion_handler) {
auto executor = boost::asio::get_associated_executor(
completion_handler, socket.get_executor());
socket.async_receive(buffers, boost::asio::bind_executor(executor,
std::bind(std::forward<CompletionHandler>(completion_handler), std::placeholders::_1, std::placeholders::_2)));
}
}
template<typename MessageReadHandler>
void async_receive(MessageReadHandler && handler, flags_type flags = 0) {
using type = detail::receive_op<MessageReadHandler>;
get_service().enqueue<type>(get_implementation(), detail::socket_service::op_type::read_op, std::forward<MessageReadHandler>(handler), flags);
}
The problem arises when the timer fires, the async_receive is not cancelled. I have read the Asio documentation but to no avail. What is the problem? Is the async_receive missing a method on it? I have tried to read up on what actually happens when the operation is cancelled but I have not found much. All help is appreciated.
I have tried changing the completion tokens, and defining cancellation function on to the struct which defines the async handlers, but nothing seems to work.
Minimal example of behaviour:
#pragma once
#include <azmq/socket.hpp>
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace asio::experimental::awaitable_operators;
auto timer(std::chrono::steady_clock::duration dur) -> asio::awaitable<void> {
asio::steady_timer timer(co_await asio::this_coro::executor);
timer.expires_after(dur);
co_await timer.async_wait(asio::use_awaitable);
}
auto foo(asio::io_context &ctx) -> asio::awaitable<void> {
azmq::sub_socket socket_{ctx};
socket_ = azmq::sub_socket(socket_.get_io_context(), true);
boost::system::error_code error_code;
std::string const socket_path{"ipc:///tmp/not_real"};
if (socket_.connect(socket_path, error_code)) {
std::exit(-1);
}
if (socket_.set_option(azmq::socket::subscribe(""), error_code)) {
std::exit(-1);
}
std::array<std::byte, 1024> buffer{};
std::cout << "waiting for async_receive" << std::endl;
co_await(azmq::async_receive(socket_, asio::buffer(buffer), asio::use_awaitable) || timer(std::chrono::seconds{1}));
std::cout << "async_receive finished" << std::endl;
}
auto main() -> int {
boost::asio::io_context ctx{};
boost::asio::co_spawn(ctx, foo(ctx), asio::detached);
ctx.run();
return 0;
}