To begin: I have read many posts about the occurence of this error (e.g. boost::mutex::~mutex(): Assertion `!pthread_mutex_destroy(&m)' failed ) and as I can see they do not apply in my case.
Additionally I can not use RAII as often suggested in those posts.
Futher I can not give a "minimum compiling example" since this error does not occur there.
My Problem: I have two mutexes in a class representing a FIFO-List the mutexes are used to lock the anchor-pointer and the back-pointer.
At that moment when the class is destroyed the destruction of the back_mutex
fails after the anchor_mutex
is already destroyed. This is the error mesage:
boost::mutex::~mutex(): Assertion `!posix::pthread_mutex_destroy(&m)' failed.
POSIX-Spec says the only two cases where pthread_mutex_destroy
fails are EINVAL
, if the mutex is invalid and EBUSY
, if the mutex is locked or referenced.
Due to that knowledge i changed my Destructor to the following for testing:
template<class T>
Fifo_Emut<T>::~Fifo_Emut()
{
this->clear();
anchor_mutex.lock();
back_mutex.lock();
anchor_mutex.unlock();
back_mutex.unlock();
}
Despite that the error still constists at the same position.
As I would suggest the locking and unlocking of the mutex should fail if one of the two cases EINVAL
or EBUSY
is relevant. Also I can gurantee that the Thread calling the Destructor is the last living Thread all others joined before.
As an additional test I wrote a return in the first line of push_back and pop_front, then the error does not occur. It also occurs when only using push_back
For 'kind of' completeness the code using the mutexes in push_back and pop_front mehtods:
/**
* @brief appends a new list element to the back end of the list
* @param[in] data the data to be copied into the list_element
**/
template<class T>
void Fifo_Emut<T>::push_back(const T const& data)
{
back_mutex.lock();
if(back == NULL)
{
if(!anchor_mutex.try_lock())
{
//if we cannot aquire anchor_mutex we have to release back_mutex and try again to avoid a deadlock
back_mutex.unlock();
return this->push_back(data);
}
if(anchor == NULL)
{
MutexListElement<T>* tmp;
tmp = new MutexListElement<T>(data);
anchor = tmp;
back = tmp;
boost::interprocess::ipcdetail::atomic_write32(&number_of_elements, 1);
anchor_mutex.unlock();
back_mutex.unlock();
}
//else error normally handled
}
else
{
MutexListElement<T>* tmp;
back->lock();
tmp = new MutexListElement<T>(back, data);
boost::interprocess::ipcdetail::atomic_inc32(&number_of_elements);
back->unlock();
back = tmp;
back_mutex.unlock();
}
}
/**
* @brief erases the first element of the queue
* @returns a copy of the data held in the erased element
**/
template<class T>
T Fifo_Emut<T>::pop_front(void)
{
uint32_t elements = boost::interprocess::ipcdetail::atomic_read32(&number_of_elements);
if(elements == 0)
{
return NULL;
}
if(elements == 1)
{
anchor_mutex.lock();
back_mutex.lock();
if(elements == boost::interprocess::ipcdetail::atomic_read32(&number_of_elements))
{
//still the same so we can pop
MutexListElement<T>* erase = anchor;
erase->lock(); //we do not have to lock next since tis is the only one
anchor = NULL;
back = NULL;
boost::interprocess::ipcdetail::atomic_write32(&number_of_elements, 0);
anchor_mutex.unlock();
back_mutex.unlock();
T tmp = erase->getData();
erase->unlock();
delete erase;
return tmp;
}
else
{
//something has changed so we have to try again
back_mutex.unlock();
anchor_mutex.unlock();
return this->pop_front();
}
}
else
{
anchor_mutex.lock();
if(boost::interprocess::ipcdetail::atomic_read32(&number_of_elements) > 1)
{
//still more than one element in the queue so we can just pop whitout changing back pointer
MutexListElement<T>* erase = anchor;
erase->lock();
(dynamic_cast<MutexListElement<T>*>(anchor->next))->lock();
anchor = dynamic_cast<MutexListElement<T>*>(anchor->next);
anchor->prev = NULL;
boost::interprocess::ipcdetail::atomic_dec32(&number_of_elements);
anchor->unlock();
anchor_mutex.unlock();
T tmp = erase->getData();
erase->unlock();
delete erase;
return tmp;
}
else
{
//number of elements decreased to other case during locking
anchor_mutex.unlock();
return this->pop_front();
}
}
}
Question: How is it possible that a 'functioning' mutex fails on being destroyed? Or am I overseeing something here? How can I get rid of that error? What have I made wrong in my code and assumptions?