Reputation: 5585
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 ©_this);
~object();
void pass( int value);
};
object::object()
{
std::cout<<"constructing object: "<<this<<" with value: "<<this->object::value<<std::endl;
}
object::object( object const ©_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 ©_this);
~threaded();
void init( int value);
};
threaded::threaded(){}
threaded::threaded( threaded const ©_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
Reputation: 392921
You can alleviate much of this by avoiding bind
. Lambdas solve this issue - most often more efficiently:
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
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