mardi 24 octobre 2017

Debug version SEGFAULTs whereas Release version works - is it RVO?

We have a template class which owns some std::unique_ptr, some of which are boost::asio

template <class cloud_type,
          bool  keep_alive   = true,
          class socket_type  = asio_http,
          class error_handle = default_error_handler>
class callable
{
    callable() = delete;
    // other stuff here, click the link to see actual code
};

It has two constructors and we've made a wrapper function which supposedly simplifies things:

template <class cloud_type,
          bool  keep_alive   = true,
          class socket_type  = asio_http,
          class error_handle = default_error_handler,
          class ...args,
          typename = 
                typename std::enable_if<!std::is_same<cloud_type, 
                                                      cloud_batch>::value, bool>>
callable<cloud_type,keep_alive,socket_type,error_handle> 
    call(typename cloud_type::callback functor,
         args... params)
{
    return callable<cloud_type,
                    keep_alive,
                    socket_type,
                    error_handle>(functor, default_node, params...);
}

Two of us have tested this, on Ubuntu 16.04 with G++ 5.9 and Boost 1.58. When we build a Release version (with -O3) the application works fine. When however we build a Debug version, the application SEGFAULTs. The actual error I get is:

Thread 1 "human_detection" received signal SIGSEGV, Segmentation fault.
0x0000000000443128 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::get (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:305
305           { return std::get<0>(_M_t); }

This seems to originate from:

#0  0x0000000000443128 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::get (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:305
#1  0x0000000000441880 in std::unique_ptr<boost::asio::io_service, std::default_delete<boost::asio::io_service> >::operator-> (this=0x120) at /usr/include/c++/5/bits/unique_ptr.h:298
#2  0x000000000043f2b4 in noos::cloud::callable<noos::cloud::human_detection, false, noos::cloud::asio_http, noos::cloud::default_error_handler>::send (this=0x0, timeout=0) at /home/zuperath/code/noos-api-maria/./noos/cloud/callable.tpl:120

Which points to:

void callable<cloud_type,
              keep_alive,
              socket_type,
              error_handle
             >::send(unsigned int timeout)
{
    assert(socket_ && query_ && resol_ && io_);
    if (!socket_)
        throw std::runtime_error("socket not set");
    if (!io_ || !query_ || !resol_)
        throw std::runtime_error("io, query or resolver not set");
    object.fill_buffer(boost::ref(*buffer_.get()), endpoint);
    socket_->is_connected() ? 
        socket_->send(*query_.get(), *resol_.get(), timeout, *buffer_.get()) :
        socket_->begin(*query_.get(), *resol_.get(), timeout);
    io_->run();
    io_->reset(); // here !!!
}

I'm trying to understand what we're doing wrong, and I'm guessing that Release uses RVO, whereas Debug makes a copy which results in the above?

When I call the wrapper:

auto query = call<human_detection,false>(
                 [&](std::vector<noos::object::human> humans) {
                    std::cout << "Found " << humans.size() << " humans!" << std::endl;
                 }, pic);

The error persists during Debug whereas if I call the class constructor directly, the SEGFAULT goes away:

callable<human_detection,false> query([&](std::vector<noos::object::human> humans) {
                        std::cout << "Found " << humans.size() << " humans!" << std::endl;
                     }, default_node, pic);

My fear is that there is an underlying problem with the resources of the class (e.g., boost::asio::io) which is enabled by making the class copyable.

Aucun commentaire:

Enregistrer un commentaire