Since I’ve started making a little project aiming to have a crossplatform support, I chose boost 1.47 to interact with the underlying OS. My project needed some multithreading, so I made a little wrapper over boost threads to fulfill my needs.
Little I knew, boost apparently leaves the thread on memory after destructing its object(?), or then it may have some sort of memory leak possibility.
The implementation of my wrapper has a scoped_ptr of type thread, and the scoped ptr will get initialized when one calls the start() function in the wrapper class. The running thread will be stopped from main thread using thread->interrupt(), and the destructor will be called from the wrapper function. (Destructor of the thread’s procedure structure, which has operator()() in it.
Here’s the implementation of the wrapper class:
(note: i_exception and couple of other functions are parts of other project components)
#define TIMED_JOIN boost::posix_time::milliseconds(1)
namespace utils
{
struct thread_threadable
{
template<typename T> friend class ut_thread;
private:
boost::shared_ptr<thread_threadable> instance;
public:
virtual ~thread_threadable() {}
virtual void operator()() = 0;
};
template<typename T = thread_threadable>
class ut_thread
{
public:
typedef T proc_t;
private:
boost::scoped_ptr<boost::thread> thr;
boost::shared_ptr<proc_t> proc;
public:
explicit ut_thread(const boost::shared_ptr<proc_t> &procedure) : proc(procedure) {}
~ut_thread();
void start();
void stop();
bool running() const {return this->thr.get() != NULL;}
proc_t &procedure() const
{
BOOST_ASSERT(this->proc.get() != NULL);
return *this->proc;
}
};
}
typedef utils::thread_threadable threadable;
template<typename T>
utils::ut_thread<T>::~ut_thread()
{
if(this->thr.get() != NULL)
{
BOOST_ASSERT(this->proc.get() != NULL);
this->stop();
}
}
template<typename T>
void utils::ut_thread<T>::start()
{
if(this->thr.get() != NULL)
i_exception::throw_this("another thread of this procedure is already running");
if(this->proc.get() == NULL)
i_exception::throw_this("procedure object not initialized");
this->proc->instance = this->proc;
this->thr.reset(new boost::thread(boost::ref(*this->proc)));
this->thr->timed_join(TIMED_JOIN);
}
template<typename T>
void utils::ut_thread<T>::stop()
{
if(this->thr.get() == NULL)
i_exception::throw_this("no thread was running");
this->thr->interrupt();
this->proc->~T();
this->thr.reset(NULL);
}
And then by checking the functionality of this wrapper class, I made test to main.cpp:
struct my_thr : public utils::thread_threadable
{
void operator()()
{
while(true);
}
};
int main()
{
while(true)
{
utils::ut_thread<> thr(boost::shared_ptr<threadable>(new my_thr));
utils::ut_thread<> thr1(boost::shared_ptr<threadable>(new my_thr));
thr.start();
thr1.start();
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
return 0;
}
At which point I noticed that these threads do not destruct, they will stay in memory until program gets terminated. They also keep executing the ‘while(true)’ statement.
So I’m asking, what would cause this kind of behaviour? Is it something defined, or just a bug or something else?
First of all
interruptwill only stop the thread at certainìnterruption points(taken fromboost::threadsdocumentation, slightly reformated):Since you don’t have any of those in your thread execution calling
interrupt()on it should have no effect.Now for destroying the
thread:The
timed_join()you called on the thread should fail, since the thread won’t have finished it’s execution that fast. Therefore you didn’tjoin(ordetach, but that wouldn’t change the ultimate outcome) your threads, meaning they do have an associated thread of execution when they are destroyed. Therefore they are detached, meaning that they will run till they are finished even through they are no longer controllable through theboost::threadobject. Since they are executing and infinite loop, finishing their execution might take some time so to say.As a Sidenote: if you choose to change to C++11
std::threadslater, you should note that destroying those without manually callingjoin()ordetach()is not valid code.