18

I am using pthread_mutex_t in a C++ program, as follows:

class Mutex : public noncopyable
{
public:
    Mutex()
    {
        pthread_mutex_init(&m_mutex, NULL);
    }

    void acquire()
    {
        pthread_mutex_lock(&m_mutex);
    }

    void release()
    {
        pthread_mutex_unlock(&m_mutex);
    }

private:
    pthread_mutex_t m_mutex;
};

(The class is not copyable - http://www.boost.org/doc/libs/1_53_0/boost/noncopyable.hpp)

The thing that I don't understand - is it considered an error to not call pthread_mutex_destroy in the destructor? The documentation I have read does not state that destroy must be called.

Does anyone know, what does pthread_mutex_destroy actually do and under what conditions is it required?

EDIT

Does the answer for pthread_mutex_destroy also apply to pthread_cond_destroy, etc? They seem almost like useless functions to me, unless pthread_mutex_init et. al. are allocating memory? (the docs, to me, aren't entirely clear on this.)

It doesn't hurt me to call destroy anyway, so the question is largely academic.

On linux anyway, it seems destroy only sets the mutex to an invalid state:

int
__pthread_mutex_destroy (mutex)
     pthread_mutex_t *mutex;
{
  if ((mutex->__data.__kind & PTHREAD_MUTEX_ROBUST_NORMAL_NP) == 0
      && mutex->__data.__nusers != 0)
    return EBUSY;

  /* Set to an invalid value.  */
  mutex->__data.__kind = -1;

  return 0;
}

(From glibc-2.14/nptl/pthread_mutex_destroy.c).

Wayne Uroda
  • 5,025
  • 4
  • 30
  • 35
  • remark that *this* windows implementation requires destroy: ftp://sourceware.org/pub/pthreads-win32/sources/pthreads-w32-2-9-1-release/pthread_mutex_destroy.c while *this one* http://locklessinc.com/articles/pthreads_on_windows/ does not. – thang Feb 06 '13 at 06:19

3 Answers3

20

If someone provides you with a destroy function, then you are required to call it as the final action on that object before it goes out of scope.

On architectures and implementations where the API has no effect, this will be optimised away, however if the API changes in future to require cleaning up of internal state and your code does not call it, your code will now have a memory and/or resource leak.

So the simple answer is yes; you must call this API - and here's the thing - even if the API does nothing at the moment, because although the API itself is fixed forever into the future, the implementation behind the API is not.

SecurityMatt
  • 6,593
  • 1
  • 22
  • 28
  • 3
    actually, interestingly enough the documentation really *doesn't* say that you NEED to destroy. http://linux.die.net/man/3/pthread_mutex_init. seems like an oversight on the part of the person who wrote it. – thang Feb 06 '13 at 04:01
  • Create/Destroy is a common pattern in C code and most developers and documentation writers take it for granted that if they provide you with a "destroy" method that you must call it after calling init but before the object goes out of scope. – SecurityMatt Feb 06 '13 at 04:03
  • I guess what confuses me is that you can statically allocate and init the mutex, as in `pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;` - if I use that, is `pthread_mutex_destroy` necessary, or even allowed? I must be overly pedantic because I think the documentation is very light on useful information. – Wayne Uroda Feb 06 '13 at 04:03
  • @WayneUroda: Yes, even then you need to call it: "In cases where default mutex attributes are appropriate, the macro PTHREAD_MUTEX_INITIALIZER can be used to initialize mutexes that are statically allocated. The effect shall be equivalent to dynamic initialization by a call to pthread_mutex_init() with parameter attr specified as NULL, except that no error checks are performed." (http://linux.die.net/man/3/pthread_mutex_destroy) – SecurityMatt Feb 06 '13 at 04:06
  • *most*. when you specify an api, you have to put everything in there, even if it is most likely *taken for granted*. That's the whole point. in fact, for AIX's manual, it does say that you need to destroy. in other OS (can't recall which, but I remember seeing, I'll try to search for it), it specifically says you only need to destroy if it's not initialized statically. in either case, when you write a specification for an API, *NOTHING* should be taken for granted. – thang Feb 06 '13 at 04:06
  • I really wonder if the pthread functions are storing the pointer to my mutex in a global table, as the docs say you cannot use a copy of mutex in any functions. In this case, I guess that destroy could remove the reference from the table? – Wayne Uroda Feb 06 '13 at 04:08
  • @thang: The API is pthread_mutex_init initializes the pthread_mutex and pthread_mutex_destroy destroys the mutex. The name conveys meaning and is part of the API. – SecurityMatt Feb 06 '13 at 04:08
  • @WayneUroda: How the pthread_mutex APIs work under the hood is implementation defined. Write code to the API, don't assume that it works one way or another because apart from anything else, POSIX doesn't have one single implementation. – SecurityMatt Feb 06 '13 at 04:09
  • Devil's advocate: it's possible that the mutex object gets destroyed automatically when it goes out of scope, or that it gets destroyed after so many seconds. The destroy function can be provided in case you want to control destruction... the question is not what do the init and destroy functions do. We know what they do. It's what happens if you call init and not call destroy? and is it necessary to call destroy once you init? sure, I would guess that you probably need to destroy, but depending on what environment you're used to, you can guess different things. – thang Feb 06 '13 at 04:13
  • 2
    @thang: A pthread_mutex is a struct, not a C++ class. It has no C++ destructor. That's why pthread_mutex_destroy exists. The question of what happens when you call pthread_mutex_init and don't call pthread_mutex_destroy is implementation defined. On some platforms nothing bad will happen. On other systems, it might cause your long running server process to crash. That's why you don't program to the implementation. You program to the API. And the API says you call pthread_mutex_destroy when you're done. If you program to the API, you're more likely that your code will actually work in the wild. – SecurityMatt Feb 06 '13 at 04:17
  • first of all, structs can have destructors. you're right that it is in fact a c struct, but when used in c++, nobody's gonna dig into the h file or the actual library to find out. this is implementation detail anyway. just saying that it is a struct is conveying implementation detail. *the API DOESN'T say you call destroy when you're done*. **that is the whole point!** it should, but it doesn't. it just says we provide 2 functions and here's what they do. it's an oversight. – thang Feb 06 '13 at 04:19
  • @thang: Structs don't have destructors in C. And pthread_mutex isn't a C++ only object. And the API does say you need to destroy it. It's right there in the title of the function. pthread_mutex_destroy. The docs are not the only part of the API. – SecurityMatt Feb 06 '13 at 04:21
  • I know that, but structs can have destructor in c+++. the specification doesn't say that it's a c struct. that is an implementation detail that I try not to use... isn't that your point? the title of the function just says it destroys. it doesn't say you need to destroy. if it were pthread_mutex_destroy_must_call_me_if_you_called_init, then yea, i agree. – thang Feb 06 '13 at 04:24
  • 1
    Thanks for the lively debate. The answer is of course yes, because calling destroy costs me very little while avoiding potential problems. I suppose this question was driven more by my curiosity as to what is actually happening (and my suspicion that destroy does actually nothing at all), but now I feel rather silly even asking the question - I just didn't get a straight answer from the docs, so I came here. – Wayne Uroda Feb 06 '13 at 04:24
  • @WayneUroda, in fact it is not true that destroy does nothing. as mentioned, it depends on implementation. in windows, i know for sure that in windows it does something. the mutex object actually maps to the windows mutex object and destroy does a closehandle. i suspect that pthread also got its origin from user space thread implementations, so things like static initializer can creep in. actually, i think this is a very astute observation of the doc that a lot of people take for granted. – thang Feb 06 '13 at 04:26
  • @thang to be honest though, I think that if a particular implementation requires that destroy is called, perhaps that implementation is in violation of the docs since the docs don't state that destroy must be called. Especially if static initialistion is an option - to me it makes no sense to call destroy on something which was created statically. When I read the docs I get the impression that destroy is only used to set the mutex to an invalid state, perhaps so future uses of it will cause an error condition? I'm still not convinced that it is required! (and I know I am being a pain now) ;) – Wayne Uroda Feb 06 '13 at 05:04
  • @WayneUroda: In some versions pthread_mutex_delete is basically a no-op (http://tima-sls.imag.fr/viewgit/general/?a=viewblob&p=Mutek&h=1b41cf149a7026eb70d5b52263ae3e6b9c5d53ea&f=kernel/pthread_mutex_destroy.c). In other implementations failure to call it causes a handle leak (https://github.com/overtone/pthreads-win32/blob/master/pthread_mutex_destroy.c). If you leak too many handles in your process it will crash. – SecurityMatt Feb 06 '13 at 05:14
  • keeping with my pedantic self, leaking too many handles won't crash your process. it'll just make it so you won't be able to allocate more handles :p – thang Feb 06 '13 at 05:20
  • out of more curiosity, is static init of a mutex under win32 pthreads not allowed? Or does it automatically allocate the handles etc for you the first time you use a mutex function? I'll agree that this question has brought out the pedant in us all. I will chill out now :) – Wayne Uroda Feb 06 '13 at 05:26
  • @WayneUroda: How it works is implementation defined. In practice, what happens is that the mutex is lazilly initialized on first use. – SecurityMatt Feb 06 '13 at 05:37
  • @SecurityMatt and WayneUroda, this guy found a loophole in win cs implementation where the static init fits in nicely (http://locklessinc.com/articles/pthreads_on_windows/). although some pthread implementations let you do cross process mutex. for these, win mutex is used. this guy, for example, (http://www2.chrishardick.com:1099/Notes/Computing/C/pthreads/mutexes.html) thinks that you only need to destroy when you're using dynamic init- **mutexes which are initialized statically do NOT need to be destroyed**. you see why it's important to explicitly state everything when you define an API? – thang Feb 06 '13 at 05:45
  • @thang: The documentation clearly says "In cases where default mutex attributes are appropriate, the macro PTHREAD_MUTEX_INITIALIZER can be used to initialize mutexes that are statically allocated. *The effect shall be equivalent to dynamic initialization by a call to pthread_mutex_init() with parameter attr specified as NULL, except that no error checks are performed.*". Or in other words, that implementation was wrong (since they aren't equivilent in that implementation). – SecurityMatt Feb 06 '13 at 05:49
  • @SecurityMatt except it doesn't say that you need to destroy :p My point is that it leaves room for interpretation, and clearly people have interpreted things differently. and yet another version: http://www.cs.ucsb.edu/~tyang/class/pthreads/pthread_mutex.txt (*Either pthread_mutex_destroy() or mutex_destroy() destroys the mutex object referenced by mp; the mutex object becomes uninitialized. The space used by the destroyed mutex **vari- able is not freed**. It needs to be explicitly reclaimed.*). – thang Feb 06 '13 at 05:52
  • @thang: That documentation says that pthread_destroy does not call free on the pointer. Nothing more. Anyway, I'm bored of this discussion. The answer is you need to call pthread_mutex_destroy on every pthread_init'ed mutex. Since the documentation clearly states that statically allocated mutexes are equviilent to calling pthread_mutex_init, you need to call pthread_mutex_destroy on those too. – SecurityMatt Feb 06 '13 at 05:55
  • @SecurityMatt, *Either pthread_mutex_destroy()... destroys the mutex object ...; the mutex object becomes uninitialized* sounds like something more to me. And why is it that specific implementations (e.g. AIX) feel the need to add a clause saying that you need to destroy? In fact, suppose you are right that the mutex gets initialized on first use, so I statically initialized a mutex m, then I call lock on it from two different threads. which thread one is responsible for actually initializing it? wouldn't you have a race condition? would it internally create a mutex to guard that mutex? – thang Feb 06 '13 at 06:01
  • @thang: How they coordinate lazy initialization is an implementation detail. It's pretty easy to avoid race-conditions by using interlocked operations to decide who does the initialization and who waits for the initialization to complete. That some particular implementations feel the need to clarify the API that destruction is not a no-op is up to them. But people who fail to call it are writing non-portable non forwards-compatible code. – SecurityMatt Feb 06 '13 at 06:07
  • @SecurityMatt, I guess the original question was never answered -- where does it say that... i guess we'll have to take it on your authority :p we're going in circle now, so I will stop it here. – thang Feb 06 '13 at 06:13
  • It says it in the function name. Posix is a C api, so there are no auto-destructors. You call posix_mutex_destroy to deinitialize a posix_mutex that you've initialized with posx_mutex_init. It's the same basic C create/delete pattern used elsewhere in POSIX (every object you manage the memory for you explicitly init and destroy, and everything you don't has a function to create and another to free). Hence, if you initialized it a posix_mutex either statically or dynamically you need to destroy it to satisfy the API without a mutex leak. – SecurityMatt Feb 06 '13 at 06:29
  • 1
    This entire discussion is silly. The right and sure-fire way to destroy a mutex is to unplug the system. – BoltClock Feb 06 '13 at 12:51
8

From IEEE documentation which is the standard governing POSIX:

The pthread_mutex_destroy() function shall destroy the mutex object referenced by mutex; the mutex object becomes, in effect, uninitialized. An implementation may cause pthread_mutex_destroy() to set the object referenced by mutex to an invalid value. A destroyed mutex object can be reinitialized using pthread_mutex_init(); the results of otherwise referencing the object after it has been destroyed are undefined.

The documentation does not say you must call it. But it is a good practice to do so.
Calling this api will signal the POSIX library to release all the resources which were reserved for use of this particular mutex object during its initialization.
It is logical to assume mutex initialization does allocate/reserve some resources.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 3
    actually the document doesn't even say that it is good practice to do so :p I think it's oversight on the person who wrote it. in windows for example, http://msdn.microsoft.com/en-us/library/windows/desktop/ms682411(v=vs.85).aspx. *Use the CloseHandle function to close the handle. The system closes the handle automatically when the process terminates. The mutex object is destroyed when its last handle has been closed.* – thang Feb 06 '13 at 04:09
  • @thang: Well I am not sure if it is an insight but to me it seems pretty logical to do so. And if one indeed wants to be pedantic and delve deep in to it, then it is easy to write a small sample program. Run it under valgrind and also check system resources with the function being called and without the function being called. – Alok Save Feb 06 '13 at 04:11
  • well you've read the c++ standards. look at all the details with respect to the way things are specified. nothing is left to the imagination... why is this an exception? – thang Feb 06 '13 at 04:14
  • @thang: C and C++ standards are pretty much comprehensive probably because large number of compiler implementations need to adhere to them while it is not the case with IEEE. This is just an guess. I have no authority to claim so with conviction. Besides I don't think the exact reasoning of *Why this is an exception* can be authoritatively answered only by someone on the IEEE committee. – Alok Save Feb 06 '13 at 04:20
  • 1
    i suspect it's just a small oversight. in fact, as I mentioned below, in other OSes (er. at least one other: http://pic.dhe.ibm.com/infocenter/aix/v7r1/index.jsp?topic=%2Fcom.ibm.aix.genprogc%2Fdoc%2Fgenprogc%2Fmutexes.htm *Like any system resource that can be shared among threads, a mutex allocated on a thread's stack must be destroyed before the thread is terminated*) actually does explicitly state this in their documentation. – thang Feb 06 '13 at 04:22
5

A few years have gone by and @SecurityMatt was right. To settle the debate, you must call pthread_mutex_destroy to satisfy API requirements and to potentially free memory.

Here is an excerpt of the latest pthread_mutex_destroy:

int _pthread_mutex_destroy (pthread_mutex_t *mutex)
{
  if (mutex->__attr == __PTHREAD_ERRORCHECK_MUTEXATTR
      || mutex->__attr == __PTHREAD_RECURSIVE_MUTEXATTR)
    /* Static attributes.  */
    ;
  else
    free (mutex->__attr);

  return 0;
}
tothphu
  • 899
  • 12
  • 21
  • 2
    No, Recall that there are three types of pthread mutex, PTHREAD_ERRORCHECK, and PTHREAD_RECURSIVE which are dealt by the if, and finally PTHREAD_NORMAL for which __attr is null. Meaning the shown _pthread_mutex_destroy implementation is equivalent to {}. – midjji Nov 18 '22 at 19:37