Opened 7 years ago

Last modified 7 years ago

#11991 new Bugs

Application crashes when yield/resume an coroutine after handing an exception

Reported by: misko.pawel@… Owned by: chris_kohlhoff
Milestone: To Be Determined Component: asio
Version: Boost 1.61.0 Severity: Problem
Keywords: asio coroutine windows Visual Studio 2015 Cc:

Description

#include <iostream>
#include <stdexcept>
#include <thread>
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>

int main()
{
    using namespace boost::asio;
    using std::chrono::seconds;
    using yield_completion_t = detail::async_result_init< yield_context, void () >;

    io_service ios;

    spawn( ios, [&ios]( auto yield )
    {
        try
        {
            throw std::runtime_error{ "" };
        }
        catch( ... )
        {
            std::cerr << "[1] " << std::endl;

            yield_completion_t completion{ yield_context{ yield } };
            auto handler = completion.handler;

            ios.post( [=]
            {
                std::this_thread::sleep_for( seconds{ 1 } );

                asio_handler_invoke( handler, &handler );
            } );

            completion.result.get();

            std::cerr << "[2] " << std::endl;
        }

    } );

    // needs more than one thread
    std::thread t{ [&]{ ios.run(); } };

    ios.run();
    t.join();

    return 0;
}

Prints:

[1]
[2]

and crashes.

Debugger points to _FindAndUnlinkFrame (inside trnsctrl.cpp file): pCurFrameInfo->pNext causes AV because pCurFrameInfo is null.

VS 2015, Win7

Change History (1)

comment:1 by olli, 7 years ago

Component: coroutineasio
Owner: changed from olli to chris_kohlhoff

You must not install/call an async_result inside a exception handler (catch-block). The exception handler might reside on the stack or might removed from the stack (including the variables) if left. Especially MS is unspecific what happens under the hood (in contrast to C++-Itanium exception ABI).

A small modification of your code prevents the error (async_result is allocated outside the exception handler):

spawn( ios, [&ios]( auto yield ){
        bool failed = false;
        try{
            throw std::runtime_error{ "" };
        }catch( ... ){
            std::cerr << "[1] " << std::endl;
            failed = true;
        }
        if (failed){
            yield_completion_t completion{ yield_context{ yield } };
            auto handler = completion.handler;
            ios.post( [=]
            {
                std::this_thread::sleep_for( seconds{ 1 } );
                asio_handler_invoke( handler, &handler );
            } );
            completion.result.get();
            std::cerr << "[2] " << std::endl;
        }
    } );

Note: See TracTickets for help on using tickets.