Search code examples
c++boostsegmentation-faultshared-ptr

C++: boost io_context/strand wrap causes segment fault on shared_ptr


Could someone help me understand why I got the following result from the code snippet? If change the lambda function to take at least 1 parameter: I got output "valid ptr" twice. This was found in a segment fault crash I had in my code.

Output:
  valid ptr
  nullptr

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

auto getWrapper(boost::asio::io_context& ioContext) 
{
    auto sharedPtr = std::make_shared<int>(0);
    return ioContext.wrap( [sharedPtr](){ 
                                          if(sharedPtr == nullptr) 
                                            std::cout << "nullptr\n";
                                          else
                                            std::cout << "valid ptr\n";
                                         });
}

int main()
{
    boost::asio::io_context ioContext;
    auto wrapper = getWrapper(ioContext);
    wrapper();
    wrapper();
    ioContext.run();
}

Solution

  • First you have to actually use a strand and io_context::wrap is deprecated. So lets write

    #include <iostream>
    #include <memory>
    #include <boost/asio.hpp>
    
    auto getScheduleWrapper(boost::asio::io_context::strand& strand)
    {
        auto sharedPtr = std::make_shared<int>(0);
        return [sharedPtr, &strand] () {
        boost::asio::post(strand.context(),
                          boost::asio::bind_executor(strand, [sharedPtr] (){
                              if(sharedPtr == nullptr)
                                  std::cout << "nullptr\n";
                              else
                                  std::cout << "valid ptr\n";
                          }));
            };
    }
    
    int main()
    {
        boost::asio::io_context ioContext;
        boost::asio::io_context::strand strand{ioContext};
        auto scheduleWrapper = getScheduleWrapper(strand);
        scheduleWrapper();
        scheduleWrapper();
    
        ioContext.run();
    }
    

    This will output

    valid ptr
    valid ptr
    

    as expected. But note: strands are not guaranteeing that scheduled tasks will run once, when in fact you are scheduling them twice. They are "only" guaranteeing that only one task will be running at a time on that strand, and that they will run in the order as they were originally scheduled.