Boost logo

Boost :

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…


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk