Crash in boost::function destructor in my thread pool implementation

Hi I am trying to implement thread pool in C++ using pthread and boost::function as a wrapper for any function that client would like to call in the worker thread. I want to encapsulate logic related to threads management in one object which is taking ownership of these threads. That means whenever this object is destroyed, threads must be stopped and cleaned up. I've been testing my code and it turns out that I get segmentation fault when I destroy WorkerThreadManager object while there is boost::function processed in worker thread. See the code in attachment and backtrace from GDB below. I don't really understand why it happens, as far as I know boost::function is copyable, so once I get a copy of it from the queue, I can pop() it and even destroy whole queue (I prooved that in some small test) and then call the function's copy. So why is destruction of std::queue< boost::function<void() > causing problems? Or maybe it is something else. (gdb) bt #0 0xb7ad33a0 in ?? () from /lib/i386-linux-gnu/libc.so.6 #1 0x0807d3a7 in boost::function0<void>::clear (this=0x858db48) at /home/marcin/intel_build/boost_1_42_0/boost/function/function_template.hpp:856 #2 0x0807d17b in boost::function0<void>::~function0 (this=0x858db48, __in_chrg=<optimized out>) at /home/marcin/intel_build/boost_1_42_0/boost/function/function_template.hpp:752 #3 0x0807cec5 in boost::function<void()>::~function(void) (this=0x858db48, __in_chrg=<optimized out>) at /home/marcin/intel_build/boost_1_42_0/boost/function/function_template.hpp:1043 #4 0x0807ced8 in std::_Destroy<boost::function<void ()> >(boost::function<void ()>*) (__pointer=0x858db48) at /usr/include/c++/4.6/bits/stl_construct.h:94 #5 0x0807c868 in std::_Destroy_aux<false>::__destroy<boost::function<void ()>*>(boost::function<void ()>*, boost::function<void ()>*) (__first=0x858db48, __last=0x858d928) at /usr/include/c++/4.6/bits/stl_construct.h:104 #6 0x0807bd05 in std::_Destroy<boost::function<void ()>*>(boost::function<void ()>*, boost::function<void ()>*) (__first=0x858d938, __last=0x858d928) at /usr/include/c++/4.6/bits/stl_construct.h:127 #7 0x0807af23 in std::_Destroy<boost::function<void ()>*, boost::function<void ()> >(boost::function<void ()>*, boost::function<void ()>*, std::allocator<boost::function<void ()> >&) (__first=0x858d938, __last=0x858d928) at /usr/include/c++/4.6/bits/stl_construct.h:153 #8 0x0807a037 in std::deque<boost::function<void ()>, std::allocator<boost::function<void ()> > >::_M_destroy_data_aux(std::_Deque_iterator<boost::function<void ()>, boost::function<void ()>&, boost::function<void ()>*>, std::_Deque_iterator<boost::function<void ()>, boost::function<void ()>&, boost::function<void ()>*>) (this=0x858beec, __first=..., __last=...) at /usr/include/c++/4.6/bits/deque.tcc:795 #9 0x08076153 in std::deque<boost::function<void ()>, std::allocator<boost::function<void ()> > >::_M_destroy_data(std::_Deque_iterator<boost::function<void ()>, boost::function<void ()>&, boost::function<void ()>*>, std::_Deque_iterator<boost::function<void ()>, boost::function<void ()>&, boost::function<void ()>*>, std::allocator<boost::function<void ()> > const&) (this=0x858beec, __first=..., __last=...) at /usr/include/c++/4.6/bits/stl_deque.h:1816 #10 0x08073411 in std::deque<boost::function<void()>, std::allocator<boost::function<void()> > >::~deque(void) (this=0x858beec, __in_chrg=<optimized out>) at /usr/include/c++/4.6/bits/stl_deque.h:898 #11 0x0806a355 in std::queue<boost::function<void()>, std::deque<boost::function<void()>, std::allocator<boost::function<void()> > > >::~queue(void) (this=0x858beec, __in_chrg=<optimized out>) at /usr/include/c++/4.6/bits/stl_queue.h:92 #12 0x0815a054 in WorkerThreadManager::~WorkerThreadManager (this=0x858be98, __in_chrg=<optimized out>) at WorkerThreadManager.cpp:42 #13 0x0815a1e3 in WorkerThreadManager::~WorkerThreadManager (this=0x858be98, __in_chrg=<optimized out>) at WorkerThreadManager.cpp:56 #14 0x080c6c51 in std::auto_ptr<WorkerThreadManager>::reset (this=0x85463e4, __p=0x0) at /usr/include/c++/4.6/backward/auto_ptr.h:244 #15 0x080604a9 in main () Regards Marcin Adamski

AMDG On 09/17/2012 01:01 PM, Marcin Adamski wrote:
Hi
I am trying to implement thread pool in C++ using pthread and boost::function as a wrapper for any function that client would like to call in the worker thread. I want to encapsulate logic related to threads management in one object which is taking ownership of these threads. That means whenever this object is destroyed, threads must be stopped and cleaned up.
I've been testing my code and it turns out that I get segmentation fault when I destroy WorkerThreadManager object while there is boost::function processed in worker thread. See the code in attachment and backtrace from GDB below.
I don't really understand why it happens, as far as I know boost::function is copyable, so once I get a copy of it from the queue, I can pop() it and even destroy whole queue (I prooved that in some small test) and then call the function's copy. So why is destruction of std::queue< boost::function<void() > causing problems? Or maybe it is something else.
You have a race condition in your destructor. pthread_cancel, does *not* wait for the thread to terminate. You need to join the threads before the queue is destroyed. In Christ, Steven Watanabe

Steven Watanabe <watanabesj@gmail.com> wrote: You have a race condition in your destructor. pthread_cancel, does *not* wait for the thread to terminate. You need to join the threads before the queue is destroyed. It's funny and actualy quite often - while I was writing response to this quotation I found a bug, thank you! So I was about to write: "The race is only between cancellation point after sem_wait() call and manager->m_results.pop() in WorkerThreadManager::WorkerThread(), and my example uses sleep to be "sure" that WorkerThreadManager destruction occurs when A::Fun() is during sleep, so there is no access to queue". And the bug is trivial: I do pop() on an empty queue, I inteded to do it on m_tasks instead of m_results... But why this bug caused crash when queue was being destructed? Before I fixed this bug I also tried joining threads after cancellation, but it caused pthread_join to hang on a thread that was during task execution. Why did it happen? I use deferred cancellation so thread should be gently cancelled on sleep() which is a cancellation point. Regards Marcin Adamski
participants (2)
-
Marcin Adamski
-
Steven Watanabe