chriskohlhoff / asio

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

Nested boost::asio::spawn and Valgrind #1454

Closed OleStauning closed 3 months ago

OleStauning commented 3 months ago

I am trying to debug a larger program using boost asio that runs for a while and then crashes is various ways. I have tried to locate the bug using Valgrind and have reproduced a minimal example with a nested boost::asio::spawn where Valgrind says there is an invalid read.

The example is:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

int main()
{
    boost::asio::io_context io_context;

    boost::asio::spawn(io_context, 
        [&](boost::asio::yield_context yield) 
        {
            std::cout<<"Spawn #1"<<std::endl;

            boost::asio::spawn(yield, // Have also tried with io_context
                [](boost::asio::yield_context yield2) 
                {
                    std::cout<<"Spawn #2"<<std::endl;
                });
        });

    io_context.run();

    return 0;
}

Running the example with valgrind gives:

$ valgrind ./nestspawn 
==377032== Memcheck, a memory error detector
==377032== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==377032== Using Valgrind-3.23.0.GIT and LibVEX; rerun with -h for copyright info
==377032== Command: ./nestspawn
==377032== 
==377032== Warning: client switching stacks?  SP change: 0x1ffefff948 --> 0x4e0d6f8
==377032==          to suppress, use: --max-stackframe=137340330576 or greater
==377032== Warning: client switching stacks?  SP change: 0x4e0d408 --> 0x1ffefff948
==377032==          to suppress, use: --max-stackframe=137340331328 or greater
==377032== Warning: client switching stacks?  SP change: 0x1ffefff7a8 --> 0x4e0d408
==377032==          to suppress, use: --max-stackframe=137340330912 or greater
==377032==          further instances of this message will not be shown.
Spawn #1
==377032== Invalid read of size 8
==377032==    at 0x1129B6: void boost::coroutines::detail::trampoline_pull<boost::coroutines::detail::pull_coroutine_object<boost::coroutines::push_coroutine<void>, void, boost::asio::detail::spawned_coroutine_thread::entry_point<boost::asio::detail::old_spawn_entry_point<boost::asio::any_io_executor, main::{lambda(boost::asio::basic_yield_context<boost::asio::any_io_executor>)#1}::operator()(boost::asio::basic_yield_context<boost::asio::any_io_executor>) const::{lambda(boost::asio::basic_yield_context<boost::asio::any_io_executor>)#1}, void (*)()> >, boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits> > >(boost::context::detail::transfer_t) (trampoline_pull.hpp:32)
==377032==    by 0x4CFC1F6: make_fcontext (in /usr/local/lib/libboost_context.so.1.85.0)
==377032==  Address 0x4e0d120 is 63,760 bytes inside a block of size 65,536 alloc'd
==377032==    at 0x484880F: malloc (vg_replace_malloc.c:446)
==377032==    by 0x124D93: boost::coroutines::basic_standard_stack_allocator<boost::coroutines::stack_traits>::allocate(boost::coroutines::stack_context&, unsigned long) (standard_stack_allocator.hpp:52)
==377032==    by 0x112B0F: boost::coroutines::pull_coroutine<void>::pull_coroutine<boost::asio::detail::spawned_coroutine_thread::entry_point<boost::asio::detail::old_spawn_entry_point<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul> >, main::{lambda(boost::asio::basic_yield_context<boost::asio::any_io_executor>)#1}, void (*)()> > >(boost::asio::detail::spawned_coroutine_thread::entry_point<boost::asio::detail::old_spawn_entry_point<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::allocator<void>, 0ul> >, main::{lambda(boost::asio::basic_yield_context<boost::asio::any_io_executor>)#1}, void (*)()> >&&, boost::coroutines::attributes const&) (asymmetric_coroutine.hpp:1474)
.... continues 

As far as I know it is legal to use nested spawn. So what is wrong in the code?

Best regards

OleStauning commented 3 months ago

Seems that boost needed to be configured with valgrind=on and BOOST_USE_VALGRIND needed to be defined in the code.

Now the example runs with valgrind without any errors.