Timmmm February 2016

Safely interrupt C++11 blocking operation

I have a std::thread that uses Boost's asio to read from a serial port:

std::atomic<bool> quit(false);

void serialThread()
{
    try
    {
        asio::io_service io;
        asio::serial_port port(io);

        port.open("COM9"); // Yeay no port enumeration support!

        port.set_option(asio::serial_port_base::baud_rate(9600));

        while (!quit)
        {
            asio::streambuf buf;
            asio::read_until(port, buf, "\n");

            auto it = asio::buffers_begin(buf.data());
            string line(it, it + buf.size());

            doStuffWithLine(line);
        }
    }
    catch (std::exception e)
    {
        cout << "Serial thread error: " << e.what() << endl;
    }
}

void SetupSignals()
{
    // Arrange it so that `quit = true;` happens when Ctrl-C is pressed.
}

int main(int argc, char *argv[])
{
    SetupSignals();

    thread st(serialThread);

    st.join();

    return 0;
}

When I press Ctrl-C I want to cleanly exit the thread, so that all destructors are called appropriately (some drivers on Windows hate it if you don't close their resources properly).

Unfortunately as you can see, the current code blocks in read_until() so when you press Ctrl-C nothing will happen until a new line of text is received.

One solution is to use polling, something like this:

asio::async_read_until(port, buf, "\n", ...);
while (!quit)
    io.poll();

But I'd rather not use polling. It is pretty inelegant. The only solution I can currently see is to have a std::condition_variable quitOrIoFinished that is triggered either when quit is set to true, or when the read finishes. But I didn't write asio so I can't give it a condition variable to wait on.

Is there any clean sane solution? In Go I would just use a select to wait on multiple channels, whe

Answers


sehe February 2016

Use an asio::signal_set to await the INT signal (control-C tends to send interrupt).

When it arrives, simply call cancel() on your IO objects with pending asynchronous operations. They will return with error_code equal to boost::asio::error::operation_aborted.

Now, if you have a io_service::work object, destruct it and the all threads running io_service::run() will return, so you can join them.

Note Take care of synchronizing access to your IO objects (e.g. when you invoke cancel() on them) because these objects are not thread-safe, unlike io_service and strand.

Post Status

Asked in February 2016
Viewed 3,674 times
Voted 12
Answered 1 times

Search




Leave an answer