C ++ pthread blocks queue stub (I think)

I have a problem with pthreads where I think I have a dead end. I created a blocking queue that I thought worked, but after doing a few more tests, I found that if I try to cancel several threads that block key_lock, I seem to be at a dead end.

The lock queue is very simple and looks like this:

template <class T> class Blocking_Queue { public: Blocking_Queue() { pthread_mutex_init(&_lock, NULL); pthread_cond_init(&_cond, NULL); } ~Blocking_Queue() { pthread_mutex_destroy(&_lock); pthread_cond_destroy(&_cond); } void put(T t) { pthread_mutex_lock(&_lock); _queue.push(t); pthread_cond_signal(&_cond); pthread_mutex_unlock(&_lock); } T pull() { pthread_mutex_lock(&_lock); while(_queue.empty()) { pthread_cond_wait(&_cond, &_lock); } T t = _queue.front(); _queue.pop(); pthread_mutex_unlock(&_lock); return t; } priavte: std::queue<T> _queue; pthread_cond_t _cond; pthread_mutex_t _lock; } 

For testing, I created 4 threads that pull this blocking queue. I added several print statements to the lock queue, and each thread falls into the pthread_cond_wait () method. However, when I try to call pthread_cancel () and pthread_join () in each thread, the program just hangs.

I also tested this with only one thread, and it works great.

According to the documentation, pthread_cond_wait () is a cancellation point, so canceling a call in these threads should cause execution to stop (and this only works with one thread). However, pthread_mutex_lock is not a cancellation point. Can something happen in the same order when pthread_cancel () is called, the canceled thread passes the mutex before terminating and does not unlock it, and then when the next thread is canceled, it cannot use the mutex and deadlocks? Or is there something else I'm doing wrong.

Any advice would be great. Thanks:)

+6
c ++ pthreads deadlock blockingqueue
source share
3 answers

pthread_cancel() best avoided.

You can unlock all of your threads blocked in Blocking_Queue :: pull () by throwing an exception there.

One of the weak points in the queue is that T t = _queue.front(); calls the copy constructor T, which can throw an exception, making you a mutex in the queue forever. Better use locks with C ++.

Here is an example of a graceful thread termination:

 $ cat test.cc #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <boost/thread/condition_variable.hpp> #include <exception> #include <list> #include <stdio.h> struct BlockingQueueTerminate : std::exception {}; template<class T> class BlockingQueue { private: boost::mutex mtx_; boost::condition_variable cnd_; std::list<T> q_; unsigned blocked_; bool stop_; public: BlockingQueue() : blocked_() , stop_() {} ~BlockingQueue() { this->stop(true); } void stop(bool wait) { // tell threads blocked on BlockingQueue::pull() to leave boost::mutex::scoped_lock lock(mtx_); stop_ = true; cnd_.notify_all(); if(wait) // wait till all threads blocked on the queue leave BlockingQueue::pull() while(blocked_) cnd_.wait(lock); } void put(T t) { boost::mutex::scoped_lock lock(mtx_); q_.push_back(t); cnd_.notify_one(); } T pull() { boost::mutex::scoped_lock lock(mtx_); ++blocked_; while(!stop_ && q_.empty()) cnd_.wait(lock); --blocked_; if(stop_) { cnd_.notify_all(); // tell stop() this thread has left throw BlockingQueueTerminate(); } T front = q_.front(); q_.pop_front(); return front; } }; void sleep_ms(unsigned ms) { // i am using old boost boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(ms)); // with latest one you can do this //boost::thread::sleep(boost::posix_time::milliseconds(10)); } void thread(int n, BlockingQueue<int>* q) try { for(;;) { int m = q->pull(); printf("thread %u: pulled %d\n", n, m); sleep_ms(10); } } catch(BlockingQueueTerminate&) { printf("thread %u: finished\n", n); } int main() { BlockingQueue<int> q; // create two threads boost::thread_group tg; tg.create_thread(boost::bind(thread, 1, &q)); tg.create_thread(boost::bind(thread, 2, &q)); for(int i = 1; i < 10; ++i) q.put(i); sleep_ms(100); // let the threads do something q.stop(false); // tell the threads to stop tg.join_all(); // wait till they stop } $ g++ -pthread -Wall -Wextra -o test -lboost_thread-mt test.cc $ ./test thread 2: pulled 1 thread 1: pulled 2 thread 1: pulled 3 thread 2: pulled 4 thread 1: pulled 5 thread 2: pulled 6 thread 1: pulled 7 thread 2: pulled 8 thread 1: pulled 9 thread 2: finished thread 1: finished 
+5
source share

I am not completely familiar with pthread_cancel () - I prefer collaborative termination.

Will pthread_cancel () leave your mutex locked? I suppose you need to clear the undo handler.

+1
source share

I had a similar experience with pthread_cond_wait () / pthread_cancel (). I had problems with a lock that still holds after the thread has returned for some reason, and it was not possible to unlock it, since you need to unlock the same thread that you are blocked. I noticed these errors when executing pthread_mutex_destroy (), since I had a single manufacturer, one consumer situation, so there was no deadlock.

pthread_cond_wait () should block the mutex when returning, and this could happen, but the final unlock did not work, because we forcibly canceled the thread. For security, I generally try to avoid using pthread_cancel (), as some platforms do not even support this. You can use volatile bool or atomics and check if the thread should be disabled. Thus, mutexes will also be processed cleanly.

+1
source share

All Articles