pandoragami
pandoragami

Reputation: 5585

New boost::thread causes object destructor to be called many times more than expected?

Strange behavior from the code posted below causes the objects destructor to be called more than once. While the code doesn't crash as a result I just want to find out why such behavior occurs. It's a very simple template of a normal program probably used by many people all around Boost land but I've never noticed this behavior till now.

#include <boost/thread.hpp>
#include <iostream>

class object
{
    private:

        int value;

    public:

    object();
    ~object();
    void pass( int value);
};

object::object()
{
    std::cout<<"constructing object: "<<this<<" with value: "<<this->object::value<<std::endl;
}
object::~object()
{
    std::cout<<"destructing  object: "<<this<<" with value: "<<this->object::value<<std::endl;
}
void object::pass( int value)
{
    this->object::value = value;
    std::cout<<"value passed is: "<<this->object::value<<std::endl;
}

class threaded
{
    private:

         object A;

    public:

    threaded();
    ~threaded();
    void init( int value);
};
threaded::threaded(){}
threaded::~threaded(){}
void threaded::init( int value)
{
    this->threaded::A.pass( value);
}

int main()
{
    threaded T;
    int unlucky_number = 13;

    boost::thread_group tg;

    tg.add_thread( new boost::thread( boost::bind( &threaded::init, T, unlucky_number)));

    tg.join_all();

    return 0;
}

Here's the code output.

constructing object: 0x7ffcb53157f0 with value: 0

destructing  object: 0x7ffcb53156d0 with value: 0 <----From here?
destructing  object: 0x7ffcb5315720 with value: 0
destructing  object: 0x7ffcb53157a0 with value: 0
destructing  object: 0x7ffcb5315780 with value: 0
destructing  object: 0x7ffcb5315710 with value: 0
destructing  object: 0x7ffcb53157c0 with value: 0
destructing  object: 0x7ffcb5315820 with value: 0
destructing  object: 0x7ffcb5315800 with value: 0 <----To here?

value passed is: 13
destructing  object: 0x1a5f2d8 with value: 13
destructing  object: 0x7ffcb53157f0 with value: 0

The objects destroyed after the first one constructed and the before the line value passed is: 13 must have somehow been created by a copy constructor inside the boost thread library but why were they never constructed and why no crash? Plus their values are always zero so it's not junk memory... any ideas?

So at the request of G.M. I tried to create copy constructors only to get boost related errors in the error_code.hpp.

Here's the new code plus the errors compiling it.

#include <boost/thread.hpp>
#include <iostream>

class object
{
    private:

        int value;

    public:

    object();
    object( object const &copy_this);
    ~object();
    void pass( int value);
};

object::object()
{
    std::cout<<"constructing object: "<<this<<" with value: "<<this->object::value<<std::endl;
}
object::object( object const &copy_this)
{
    std::cout<<"object copy constructor. "<<std::endl;
    this->object::value = copy_this.value;
}
object::~object()
{
    std::cout<<"destructing  object: "<<this<<" with value: "<<this->object::value<<std::endl;
}
void object::pass( int value)
{
    this->object::value = value;
    std::cout<<"value passed is: "<<this->object::value<<std::endl;
}

class threaded
{
    private:

         object A;

    public:

    threaded();
    threaded( threaded const &copy_this);
    ~threaded();
    void init( int value);
};
threaded::threaded(){}
threaded::threaded( threaded const &copy_this)
{
    std::cout<<"threaded copy constructor. "<<std::endl;
    this->threaded::A = copy_this.A;
}
threaded::~threaded(){}
void threaded::init( int value)
{
    this->threaded::A.pass( value);
}

int main()
{
    threaded T;
    int unlucky_number = 13;

    boost::thread_group tg;

    tg.add_thread( new boost::thread( boost::bind( &threaded::init, T, unlucky_number)));

    tg.join_all();
    //T.init(13);

    return 0;
}

||=== Build: Debug in boost_thread_destructor_problem (compiler: GNU GCC Compiler) ===|
obj/Debug/boost_thread_destructor_problem.o||In function `__static_initialization_and_destruction_0(int, int)':|
/usr/include/boost/system/error_code.hpp|221|undefined reference to `boost::system::generic_category()'|
/usr/include/boost/system/error_code.hpp|222|undefined reference to `boost::system::generic_category()'|
/usr/include/boost/system/error_code.hpp|223|undefined reference to `boost::system::system_category()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread_exception::thread_exception(int, char const*)':|
/usr/include/boost/thread/exceptions.hpp|51|undefined reference to `boost::system::system_category()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::condition_error::condition_error(int, char const*)':|
/usr/include/boost/thread/exceptions.hpp|84|undefined reference to `boost::system::system_category()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::detail::thread_data_base::thread_data_base()':|
/usr/include/boost/thread/pthread/thread_data.hpp|152|undefined reference to `vtable for boost::detail::thread_data_base'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::detail::interruption_checker::interruption_checker(pthread_mutex_t*, pthread_cond_t*)':|
/usr/include/boost/thread/pthread/thread_data.hpp|195|undefined reference to `boost::detail::get_current_thread_data()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread::start_thread()':|
/usr/include/boost/thread/detail/thread.hpp|179|undefined reference to `boost::thread::start_thread_noexcept()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread::~thread()':|
/usr/include/boost/thread/detail/thread.hpp|254|undefined reference to `boost::thread::detach()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread::get_id() const':|
/usr/include/boost/thread/detail/thread.hpp|741|undefined reference to `boost::thread::native_handle()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread::join()':|
/usr/include/boost/thread/detail/thread.hpp|767|undefined reference to `boost::thread::join_noexcept()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::condition_variable::wait(boost::unique_lock<boost::mutex>&)':|
/usr/include/boost/thread/pthread/condition_variable.hpp|84|undefined reference to `boost::this_thread::interruption_point()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::shared_mutex::lock_shared()':|
/usr/include/boost/thread/pthread/shared_mutex.hpp|186|undefined reference to `boost::this_thread::disable_interruption::disable_interruption()'|
/usr/include/boost/thread/pthread/shared_mutex.hpp|186|undefined reference to `boost::this_thread::disable_interruption::~disable_interruption()'|
/usr/include/boost/thread/pthread/shared_mutex.hpp|186|undefined reference to `boost::this_thread::disable_interruption::~disable_interruption()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::shared_mutex::lock()':|
/usr/include/boost/thread/pthread/shared_mutex.hpp|287|undefined reference to `boost::this_thread::disable_interruption::disable_interruption()'|
/usr/include/boost/thread/pthread/shared_mutex.hpp|287|undefined reference to `boost::this_thread::disable_interruption::~disable_interruption()'|
/usr/include/boost/thread/pthread/shared_mutex.hpp|287|undefined reference to `boost::this_thread::disable_interruption::~disable_interruption()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::thread_group::join_all()':|
/usr/include/boost/thread/detail/thread_group.hpp|117|undefined reference to `boost::thread::joinable() const'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf1<void, threaded, int>, boost::_bi::list2<boost::_bi::value<threaded>, boost::_bi::value<int> > > >::thread_data(boost::_bi::bind_t<void, boost::_mfi::mf1<void, threaded, int>, boost::_bi::list2<boost::_bi::value<threaded>, boost::_bi::value<int> > >)':|
/usr/include/boost/thread/detail/thread.hpp|109|undefined reference to `boost::detail::thread_data_base::~thread_data_base()'|
obj/Debug/boost_thread_destructor_problem.o||In function `boost::detail::thread_data<boost::_bi::bind_t<void, boost::_mfi::mf1<void, threaded, int>, boost::_bi::list2<boost::_bi::value<threaded>, boost::_bi::value<int> > > >::~thread_data()':|
/usr/include/boost/thread/detail/thread.hpp|90|undefined reference to `boost::detail::thread_data_base::~thread_data_base()'|
/usr/include/boost/thread/detail/thread.hpp|90|undefined reference to `boost::detail::thread_data_base::~thread_data_base()'|
obj/Debug/boost_thread_destructor_problem.o:(.rodata._ZTIN5boost6detail11thread_dataINS_3_bi6bind_tIvNS_4_mfi3mf1Iv8threadediEENS2_5list2INS2_5valueIS6_EENS9_IiEEEEEEEE[_ZTIN5boost6detail11thread_dataINS_3_bi6bind_tIvNS_4_mfi3mf1Iv8threadediEENS2_5list2INS2_5valueIS6_EENS9_IiEEEEEEEE]+0x10)||undefined reference to `typeinfo for boost::detail::thread_data_base'|
||error: ld returned 1 exit status|
||=== Build failed: 24 error(s), 0 warning(s) (0 minute(s), 2 second(s)) ===|

Upvotes: 3

Views: 248

Answers (2)

sehe
sehe

Reputation: 392921

You can alleviate much of this by avoiding bind. Lambdas solve this issue - most often more efficiently:

C++11

tg.create_thread([=]() mutable { T.init(unlucky_number); });

In C++14 you can be a bit more advanced:

tg.create_thread([T=std::move(T), unlucky_number = 13]() mutable { T.init(unlucky_number); });

If object is expensive to copy, consider making it move-aware, e.g.

object(object const&)       = default;
object(object&&)            = default;
object& operator=(object&&) = default;

To ensure the compiler generates the special members even though you added user-declared (non-trivial) constructor/destructor members.

Upvotes: 3

G.M.
G.M.

Reputation: 12879

Due to the way you've invoked boost::bind...

tg.add_thread(new boost::thread(boost::bind(&threaded::init, T, unlucky_number)));

T will be passed by value. That can lead to any amount of copying of T being performed by the boost::bind implementation.

Since you know that T will, in this case, outlive the thread on which it's being used you can either force a pass by reference...

tg.add_thread(new boost::thread(boost::bind(&threaded::init, boost::ref(T), unlucky_number)));

or pass a pointer...

tg.add_thread(new boost::thread(boost::bind(&threaded::init, &T, unlucky_number)));

Upvotes: 3

Related Questions