forked from aziomq/aziomq
-
Notifications
You must be signed in to change notification settings - Fork 115
Open
Description
Hi,
socket::cancel() appears to call pending completion handlers immediately, instead of delaying their execution until after the call returns. As a result there are dead locks when calling more socket operations (such as async_send()) from the completion handlers, because the socket uses a non-recursive mutex internally. This differs from other boost::asio objects such as boost::asio::steady_timer, which allow this case, so for example you can restart a timer from inside the operation_aborted completion handler.
An example to show the issue:
// Build: g++ -Wall -g azmq_cancel_timing.cpp -lzmq -lboost_filesystem -o azmq_cancel_timing
#include <azmq/socket.hpp>
#include <azmq/version.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <stdio.h>
#include <zmq.hpp>
int main()
{
printf("boost version: %i\n", BOOST_VERSION);
printf("azmq version: %i\n", AZMQ_VERSION);
boost::asio::io_context ioctx;
boost::asio::steady_timer timer(ioctx);
timer.expires_from_now(std::chrono::seconds(1));
timer.async_wait(
[](boost::system::error_code const& ec)
{
printf("timer async_wait completion handler, ec = %s\n", ec.message().c_str());
}
);
boost::asio::ip::udp::socket udpsocket(ioctx, boost::asio::ip::udp::endpoint(boost::asio::ip::make_address("127.0.0.1"), 0));
std::array<uint8_t, 1000> buffer1;
udpsocket.async_receive(boost::asio::buffer(buffer1),
[](boost::system::error_code const& ec, size_t)
{
printf("udp socket async_receive completion handler, ec = %s\n", ec.message().c_str());
}
);
azmq::socket azmqsocket(ioctx, ZMQ_PULL);
azmqsocket.set_option(azmq::socket::linger(0));
azmqsocket.connect("tcp://127.0.0.1:12345");
std::array<uint8_t, 1000> buffer2;
azmqsocket.async_receive(boost::asio::buffer(buffer2),
[](boost::system::error_code const& ec, size_t)
{
printf("azmq socket async_receive completion handler, ec = %s\n", ec.message().c_str());
}
);
printf("timer.cancel()...\n");
timer.cancel();
printf("timer.cancel()... done\n");
printf("udpsocket.cancel()...\n");
udpsocket.cancel();
printf("udpsocket.cancel()... done\n");
printf("azmqsocket.cancel()...\n");
azmqsocket.cancel();
printf("azmqsocket.cancel()... done\n");
printf("io_context.run()...\n");
ioctx.run();
printf("io_context.run()... done\n");
return 0;
}Actual output, azmq socket competion handler called during cancel(), instead of later like the others:
boost version: 108200
azmq version: 10002
timer.cancel()...
timer.cancel()... done
udpsocket.cancel()...
udpsocket.cancel()... done
azmqsocket.cancel()...
azmq socket async_receive completion handler, ec = Operation canceled
azmqsocket.cancel()... done
io_context.run()...
timer async_wait completion handler, ec = Operation canceled
udp socket async_receive completion handler, ec = Operation canceled
io_context.run()... done
Expected output: All completion handlers are called later through the io_service.
Metadata
Metadata
Assignees
Labels
No labels