Destruction of boost::mutex fails in class destructor

1.2k views Asked by At

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?

0

There are 0 answers