Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

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
private:
    // other members, most are unique pointers
    std::unique_ptr<boost::asio::io_service> io_;
};

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.

EDIT:

I solved the SEGFAULT, it was due to a lambda capturing by reference, a (this) which was being released. However, my original question still remains; why is this happening during DEBUG but not RELEASE?

like image 427
Ælex Avatar asked Jan 18 '26 15:01

Ælex


1 Answers

There is of course a fundamental problem with copying a unique_ptr, it's not possible. That is the point of being unique. The compiler enforces this by disabling its copy ctor. And the default copy constructor of callable therefore does not exist either.

But it does look like your callable can be copied. What I suspect is happening is that you accidentally wrote a converting constructor:

callable::callable(callback functor, platform = default_node);

I suspect that your callable can be converted to a callback and ends up being copied via this route.

As a rule of thumb, constructors callable with one argument (possibly after adding default values) should be explicit.

BTW: Your other ctor also has a logical error:

template <typename... parameters>
callable(callback functor,
         platform info = default_node,
         parameters... args);

Exactly how would this default value be used? It could only be used if a single argument is provided, but then overload resolution kicks in and the first ctor would be selected. That said, it is also a potential one-argument ctor in its current form and should also be explicit.

like image 116
MSalters Avatar answered Jan 20 '26 05:01

MSalters



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!