chriskohlhoff / asio

Asio C++ Library
http://think-async.com/Asio
4.97k stars 1.22k forks source link

timer does only work once when building with BOOST_ASIO_HAS_IO_URING_AS_DEFAULT #1530

Open ceggers-arri opened 2 months ago

ceggers-arri commented 2 months ago

OS: linux-6.6.49 Boost: 1.84.0

I have taken an older "blocking read with timeout" example from here: https://lists.boost.org/Archives/boost/2007/04/120339.php

This example (updated source code below) works fine as long as using the epoll reactor (simply try with a non connected UART, the program should finish after two seconds).

But as soon as I switch to the io_uring implementation (by defining BOOST_ASIO_HAS_IO_URING and BOOST_ASIO_HAS_IO_URING_AS_DEFAULT), the 2nd timeout is not reached (program exits after 301 seconds).

When I only define BOOST_ASIO_HAS_IO_URING (without BOOST_ASIO_HAS_IO_URING_AS_DEFAULT), the program works fine. Additionally I recognized that the behavior depends on the timing. When single stepping in a debugger, also the 2nd timeout works correctly.

Q: Is the example program correct? Q: Why does it not work as expected with io_uring?

#include <chrono>
#include <iostream>

#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/optional.hpp>
#include <boost/system/error_code.hpp>

using namespace boost;
using namespace boost::asio;
using namespace std::chrono;
using namespace boost::placeholders;
using namespace boost::system;

void set_result(optional<boost::system::error_code>* a, boost::system::error_code b)
{
        a->reset(b);
}

template <typename MutableBufferSequence>
void read_with_timeout(io_context& io_context, serial_port& port,
                const MutableBufferSequence& buffers)
{
        optional<error_code> timer_result;
        steady_timer timer(io_context);
        timer.expires_after(seconds(1));
        timer.async_wait(
                boost::bind(set_result, &timer_result, _1));

        optional<error_code> port_result;
        async_read(port, buffers,
                boost::bind(set_result, &port_result, _1));

        io_context.restart();
        while (io_context.run_one())
        {
                if (timer_result)
                        port.cancel();
                else if (port_result)
                        timer.cancel();
        }

        if (port_result && *port_result)
                throw system_error(*port_result);
}

int main(int argc, char *argv[])
{
        io_context io_context;
        serial_port port(io_context, "/dev/ttymxc6");
        char buf[4096];

        try
        {
                std::cout << "Read (1) ...\n";
                read_with_timeout(io_context, port, buffer(buf));
        }
        catch (const std::exception& e)
        {
                std::cerr << "Exception: " << e.what() << '\n';
        }

        try
        {
                std::cout << "Read (2) ...\n";
                read_with_timeout(io_context, port, buffer(buf));
        }
        catch (const std::exception& e)
        {
                std::cerr << "Exception: " << e.what() << '\n';
        }
}
kealqqq commented 2 months ago

I have encountered a similar problem where when I enable BOOST_ASIO_DISABLE_EPOLL and BOOST_ASIO_HAS_IO_URING, the boost::asio::steady_timer callback cycle setting does not take effect and runs every 300 seconds.