I am trying to get myself up-to-date on C++11 changes, and I’m making a thread-safe wrapper around std::queue called SafeQueue. I have two conditions to potentially block on, the queue being full and the queue being empty. I am using std::condition_variable for this. Unfortunately, on Linux, the notify_all() call on my empty condition is segfaulting. It works fine on a Mac with clang. It’s segfaulting in the enqueue() method:
#ifndef mqueue_hpp
#define mqueue_hpp
#include <queue>
#include <mutex>
//////////////////////////////////////////////////////////////////////
// SafeQueue - A thread-safe templated queue. //
//////////////////////////////////////////////////////////////////////
template<class T>
class SafeQueue
{
public:
// Instantiate a new queue. 0 maxsize means unlimited.
SafeQueue(unsigned int maxsize = 0);
~SafeQueue(void);
// Enqueue a new T. If enqueue would cause it to exceed maxsize,
// block until there is room on the queue.
void enqueue(const T& item);
// Dequeue a new T and return it. If the queue is empty, wait on it
// until it is not empty.
T& dequeue(void);
// Return size of the queue.
size_t size(void);
// Return the maxsize of the queue.
size_t maxsize(void) const;
private:
std::mutex m_mutex;
std::condition_variable m_empty;
std::condition_variable m_full;
std::queue<T> m_queue;
size_t m_maxsize;
};
template<class T>
SafeQueue<T>::SafeQueue(unsigned int maxsize) : m_maxsize(maxsize) { }
template<class T>
SafeQueue<T>::~SafeQueue() { }
template<class T>
void SafeQueue<T>::enqueue(const T& item) {
// Synchronize.
if ((m_maxsize != 0) && (size() == m_maxsize)) {
// Queue full. Can't push more on. Block until there's room.
std::unique_lock<std::mutex> lock(m_mutex);
m_full.wait(lock);
}
{
std::lock_guard<std::mutex> lock(m_mutex);
// Add to m_queue and notify the reader if it's waiting.
m_queue.push(item);
}
m_empty.notify_all();
}
template<class T>
T& SafeQueue<T>::dequeue(void) {
// Synchronize. No unlock needed due to unique lock.
if (size() == 0) {
// Wait until something is put on it.
std::unique_lock<std::mutex> lock(m_mutex);
m_empty.wait(lock);
}
std::lock_guard<std::mutex> lock(m_mutex);
// Pull the item off and notify writer if it's waiting on full cond.
T& item = m_queue.front();
m_queue.pop();
m_full.notify_all();
return item;
}
template<class T>
size_t SafeQueue<T>::size(void) {
std::lock_guard<std::mutex> lock(m_mutex);
return m_queue.size();
}
template<class T>
size_t SafeQueue<T>::maxsize(void) const {
return m_maxsize;
}
#endif /* mqueue_hpp */
Clearly I’m doing something wrong but I can’t figure it out. Output from gdb:
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x0000000000414739 in std::condition_variable::notify_all() ()
#2 0x00000000004054c4 in SafeQueue<int>::enqueue (this=0x7ffee06b3470,
item=@0x7ffee06b355c: 1) at ../mqueue.hpp:59
#3 0x0000000000404ab6 in testsafequeue () at test.cpp:13
#4 0x0000000000404e99 in main () at test.cpp:49
(gdb) frame 2
#2 0x00000000004054c4 in SafeQueue<int>::enqueue (this=0x7ffee06b3470,
item=@0x7ffee06b355c: 1) at ../mqueue.hpp:59
59 m_empty.notify_all();
(gdb) info locals
No locals.
(gdb) this.m_empty
Undefined command: "this.m_empty". Try "help".
(gdb) print this->m_empty
$1 = {_M_cond = {__data = {{__wseq = 0, __wseq32 = {__low = 0,
__high = 0}}, {__g1_start = 0, __g1_start32 = {__low = 0,
__high = 0}}, __g_refs = {0, 0}, __g_size = {0, 0},
__g1_orig_size = 0, __wrefs = 0, __g_signals = {0, 0}},
__size = '\000' <repeats 47 times>, __align = 0}}
Help appreciated.
My sample test that is crashing.
SafeQueue<int> queue(10);
queue.enqueue(1);
Segfault on the notify_all() in enqueue().