I have some ActiveMQ initialization code like this:
bool Base::init()
{
boost::thread th(boost::bind(&Derived::init_derived, this));
if(!th.timed_join(boost::posix_time::seconds(10)) {
th.interrupt();
return false;
}
return true;
}
In child class:
void Derived::init_derived()
{
try
{
m_session = connection.createSession(cms::Session::AUTO_ACKNOWLEDGE); // Line 1
}
catch(boost::thread_interrupted const&)
{
// log error...
}
}
In the caller:
template <typename Derived>
boost::shared_ptr<Derived> Base::create(Derived *obj)
{
boost::shared_ptr<Derived> inst(obj);
if(inst != nullptr && !inst->init()) {
inst.reset(); // Line 2
}
return inst;
}
The problem is Line 2
causes crash. If omitted, it's a memory leak.
If I replace Line 1
with other in-module blocking code like: for(;;){}
, it still breaks and doesn't crash. Am I not permitted to use boost thread functions outside module boundary?
How do I fix this?
CodePudding user response:
I'm not sure I can reproduce the error. Mainly because I had trouble coming up with legal code that matches your snippets. (There's a typo in it, the this
doesn't seem compatible in the bind
and it would appear that Base
is a
Here's what I came up with to make your code self-contained:
//#define BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE
#include <boost/thread.hpp>
#include <iostream>
template <typename Derived> struct Base {
virtual ~Base() = default;
bool init();
static boost::shared_ptr<Derived> create(Derived*);
};
struct Derived : Base<Derived> {
void init_derived();
};
template <typename Derived> bool Base<Derived>::init() {
boost::thread th(boost::bind(&Derived::init_derived, static_cast<Derived*>(this)));
if (!th.timed_join(boost::posix_time::seconds(1))) {
std::cerr << "init(): Timeout, interrupting" << std::endl;
th.interrupt();
//std::cerr << "init(): Interrupted, joining" << std::endl;
//th.join();
std::cerr << "init(): Return false" << std::endl;
return false;
}
std::cerr << "init(): Return true" << std::endl;
return true;
}
void Derived::init_derived() {
try {
std::cerr << "init_derived(): begin work" << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(2));
// m_session = connection.createSession(cms::Session::AUTO_ACKNOWLEDGE);
std::cerr << "init_derived(): complete work" << std::endl;
} catch (boost::thread_interrupted const&) {
std::cerr << "init_derived(): interrupted" << std::endl;
// log error...
}
}
template <typename Derived>
boost::shared_ptr<Derived> Base<Derived>::create(Derived* obj) {
boost::shared_ptr<Derived> inst(obj);
return inst && inst->init() ? inst : nullptr;
}
int main() {
auto d = Base<Derived>::create(new Derived());
}
Althought the output seems alright:
init_derived(): begin work
init(): Timeout, interrupting
init(): Return false
Things are not fine. The thread outlives the instance. You can see it because init_derived()
never prints the "init_derived(): interrupted"
trace message.
You can force your program to fail early, using
#define BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE
(Note: this is the standard behaviour for
std::thread
)
See it Live prints:
init_derived(): begin work
init(): Timeout, interrupting
init(): Return false
terminate called without an active exception
bash: line 7: 5541 Aborted (core dumped) ./a.out
There are several ways to fix it, but the simplest is quite obviously:
Join!
Uncommenting the lines
std::cerr << "init(): Interrupted, joining" << std::endl;
th.join();
adds the missing join. It should be "non-blocking" assuming that the thread_interrupted
exception is handled swiftly. See it Live printing:
init_derived(): begin work
init(): Timeout, interrupting
init(): Interrupted, joining
init_derived(): interrupted
init(): Return false
Success!
Other Observations/Ideas
It seems like really you could use a [packaged_task
][3]<shared_ptr<Base>(Args...)>
. You would have the task own the shared pointer until returned, and throw on timeout.