Subject: [boost] Correct Mutext Destroy Behaviour, Pthreads and Boost
From: Robert Bell (skyoyster_at_[hidden])
Date: 2015-06-01 15:08:23


If an object is implemented to support reference counting, and has an internal raw pthread mutex, the open group is pretty clear in the pthread_mutex_destroy doc on what different implementations must ensure (http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_mutex_destroy.html).

Note the following statement at the bottom:

"A mutex can be destroyed immediately after it is unlocked. For example, consider the following code:"

   obj_done(struct obj *op)
   {
       pthread_mutex_lock(&op->om);
       if (--op->refcnt == 0) {
           pthread_mutex_unlock(&op->om);
   (A) pthread_mutex_destroy(&op->om);
   (B) free(op);
       } else
   (C) pthread_mutex_unlock(&op->om);
   }

In this case obj is reference counted and obj_done() is called whenever a reference to the object is dropped. Implementations are required to allow an object to be destroyed and freed and potentially unmapped (for example, lines A and B) immediately after the object is unlocked (line C)."

On moving some of my underlying libraries to Boost, I took a look at Boost's Mutex object. The implementation of the Mutex object's destructor call does:

   ~mutex()
           {
             int const res = posix::pthread_mutex_destroy(&m);
             boost::ignore_unused(res);
             BOOST_ASSERT(!res);
           }

My question centres on whether Boost's implementation violates the open group's requirement. The calls to posix::pthread_mutex_destroy (Boost's wrapper), usually perform a straight-through call to ::pthread_mutex_destroy, but it occurs inside the destructor, and so does this call occur "immediately after" an unlock by the group's definition?

In straight pthreads, if I want to make sure I can rely on various implementations of the standard, I can simply do exactly what the open group says, unlock, and the next line destroy. But in the case of Boost, is it safe to do something like:

   mutex->unlock();
   delete mutex_;

One thing I did note of interest is that Apple's implementation seems to lock the mutex from within the destroy call, which is curious:

   int
   pthread_mutex_destroy(pthread_mutex_t *mutex)
   {
           int res;

           LOCK(mutex->lock);
           if (mutex->sig == _PTHREAD_MUTEX_SIG)
           {
                   if (mutex->owner == (pthread_t)NULL &&
                       mutex->busy == (pthread_cond_t *)NULL)
                   {
                           mutex->sig = _PTHREAD_NO_SIG;
                           res = ESUCCESS;
                   }
                   else
                           res = EBUSY;
           }
           else
                   res = EINVAL;
           UNLOCK(mutex->lock);
           return (res);
   }

There seems, generally, to be a good deal of confusion surrounding when it is safe to destroy the resources associated with a mutex, and most people offer widely inaccurate comments based largely on opinion. My question points to whether code adjacency is a requirement of correct behaviour in some implementations, and if not, why not?

If I have missed an obvious explanation somewhere, my apologies. I read through the different patterns of Boost threading listed in the documentation, but was not convinced the question was addressed.

Thanks in advance…