Le 01/06/15 21:08, Robert Bell a écrit :
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_destr...).
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? As with pthread, It is up to the user to ensure that the mutex is unlocked when destroyed. I don't see any problem with the current implementation.
The standard contains the sentence "The behavior of a program is undefined if it destroys a mutex object owned by any thread or a thread terminates while owning a mutex object." C++ International Standard I have not found something similar in the documentation, so it should be missing.
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.
Please, could you create a ticket so that I don't forget to update the documentation? Thanks, Vicente