Начать: Я прочитал много сообщений о возникновении этой ошибки (например, boost :: mutex :: ~ mutex (): утверждение `! pthread_mutex_destroy (&м) «не удалось ) и как я вижу они не применяются в моем случае.
Кроме того, я не могу использовать RAII, как часто предлагается в этих сообщениях.
Далее я не могу привести «минимальный пример компиляции», так как этой ошибки там не происходит.
Моя проблема:
У меня есть два мьютекса в классе, представляющем FIFO-список, мьютексы используются для блокировки указателя привязки и обратного указателя.
В тот момент, когда класс уничтожен, уничтожение back_mutex
не удается после anchor_mutex
уже разрушен. Это сообщение об ошибке:
boost::mutex::~mutex(): Assertion `!posix::pthread_mutex_destroy(&m)' failed.
POSIX-Spec говорит только два случая, когда pthread_mutex_destroy
неудачи EINVAL
если мьютекс недействителен и EBUSY
, если мьютекс заблокирован или на него ссылаются.
Благодаря этим знаниям я изменил мой деструктор для тестирования:
template<class T>
Fifo_Emut<T>::~Fifo_Emut()
{
this->clear();
anchor_mutex.lock();
back_mutex.lock();
anchor_mutex.unlock();
back_mutex.unlock();
}
Несмотря на это, ошибка все еще остается неизменной.
Как я предположил бы, блокировка и разблокировка мьютекса должны завершиться неудачей, если один из двух случаев EINVAL
или же EBUSY
актуально. Также я могу гарантировать, что Нить, вызывающая Деструктор, является последней живой Нитью, к которой присоединились все остальные.
В качестве дополнительного теста я написал возврат в первой строке push_back и pop_front, тогда ошибки не возникает. Это также происходит, когда используется только push_back
Для полноты «вида» кода используйте мьютексы в методах push_back и pop_front:
/**
* @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();
}
}
}
Вопрос:
Как возможно, что «функционирующий» мьютекс потерпит неудачу при разрушении?
Или я здесь что-то наблюдаю? Как я могу избавиться от этой ошибки? Что я сделал неправильно в своем коде и предположениях?
Задача ещё не решена.
Других решений пока нет …