Discussion:
PTHREAD_MUTEX_INITIALIZER and shared libraries
Martin Sebor
2010-04-30 18:23:26 UTC
Permalink
I'm wondering whether calling pthread_mutex_destroy on a mutex
object initialized using the PTHREAD_MUTEX_INITIALIZER macro is
necessary (or even a good idea) in dynamically loadable shared
libraries.

The specific use case that I have in mind is a library that is
repeatedly opened and closed by a process, and that contains
such a mutex object as an implementation detail.

When the library is first opened (via dlopen()), the mutex is
initialized. If, however, a process destroys the mutex before
closing the library and then proceeds to close and open it
again, is it safe to assume that the mutex will be correctly
re-initialized? If the process doesn't destroy the mutex, does
it risk leaking resources?

Since dlclose() is allowed but not required to actually unload
the library, it's not clear to me what assumptions are safe to
to make about the state of the mutex after reloading it.

Thanks
Martin
Ulrich Drepper
2010-04-30 19:12:01 UTC
Permalink
Post by Martin Sebor
I'm wondering whether calling pthread_mutex_destroy on a mutex
object initialized using the PTHREAD_MUTEX_INITIALIZER macro is
necessary (or even a good idea) in dynamically loadable shared
libraries.
I think programs are screwed if you try yo unload objects using the
initializers.

As you said, dlclose() is allowed to not do anything. An dlopen() after
that might be a no-op and specifically might not reinitialize global
variables. The semantics of the dlopen() is that global variables will
have the value from the file or those which have been previously in memory.

On the other hand, if you do *not* call pthread_mutex_destroy and the
program continues to run there might be memory locations which have been
used as a mutex object, which hasn't been deinitialized, and which is
now used for something else. In this case a checking implementation
where every call to pthread_mutex_lock checks that the mutex parameter
points to a valid, initialized pthread_mutex_t object will fail.


I think the only way out is to say that after a dlclose and subsequent
dlopen the state of a initialized global pthread_mutex_t object (and all
other sync objects) is undefined. Programmers can be expected to use
pthread_mutex_init in those cases.

- --
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Martin Sebor
2010-05-02 20:02:31 UTC
Permalink
That's what suspected, thanks for the confirmation.

It seems that in order to be portable, dynamically loadable
shared libraries must avoid using PTHREAD_MUTEX_INITIALIZER
and instead take care to initialize and destroy their mutex
objects dynamically.

This in turn requires cooperation from programs: each such
library must define a pair of initialization and finalization
functions that programs must call just after dlopen() and just
before dlclose().

Incidentally, it occurs to me that pthread_once_t suffers from
the same problem. However, unlike in the mutex case, there is
no way to re-initialize a pthread_once_t object. This rules
out the dynamic initialization strategy above. In effect,
there appears to be no safe way to dlopen a shared library
that relies on dynamically initialized global mutexes and
one-time initialization after it has been dlclosed.

(By safe I mean portable with a no-leak guarantee and no
risk of undefined behavior due to double initialization
or accessing invalid objects.)

Martin
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Post by Martin Sebor
I'm wondering whether calling pthread_mutex_destroy on a mutex
object initialized using the PTHREAD_MUTEX_INITIALIZER macro is
necessary (or even a good idea) in dynamically loadable shared
libraries.
I think programs are screwed if you try yo unload objects using the
initializers.
As you said, dlclose() is allowed to not do anything. An dlopen() after
that might be a no-op and specifically might not reinitialize global
variables. The semantics of the dlopen() is that global variables will
have the value from the file or those which have been previously in memory.
On the other hand, if you do *not* call pthread_mutex_destroy and the
program continues to run there might be memory locations which have been
used as a mutex object, which hasn't been deinitialized, and which is
now used for something else. In this case a checking implementation
where every call to pthread_mutex_lock checks that the mutex parameter
points to a valid, initialized pthread_mutex_t object will fail.
I think the only way out is to say that after a dlclose and subsequent
dlopen the state of a initialized global pthread_mutex_t object (and all
other sync objects) is undefined. Programmers can be expected to use
pthread_mutex_init in those cases.
- --
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
iEYEARECAAYFAkvbK4EACgkQ2ijCOnn/RHTfUwCfULK10ExYsffYdW1OsTy2DVtd
AvgAoJ8T/SoFdl3tfUlcDouyRW1pZYl1
=qrQG
-----END PGP SIGNATURE-----
Ulrich Drepper
2010-05-03 04:58:56 UTC
Permalink
Post by Martin Sebor
It seems that in order to be portable, dynamically loadable
shared libraries must avoid using PTHREAD_MUTEX_INITIALIZER
and instead take care to initialize and destroy their mutex
objects dynamically.
True.
Post by Martin Sebor
This in turn requires cooperation from programs: each such
library must define a pair of initialization and finalization
functions that programs must call just after dlopen() and just
before dlclose().
In portable programs. In reality I suspect implementations provide you
with constructors/destructors. These functions could then be used to
run the initialization/deinitialization. Something like this is
necessary for global C++ objects. Whether and how to hook into them
from user code is not standardized.
Post by Martin Sebor
Incidentally, it occurs to me that pthread_once_t suffers from
the same problem. However, unlike in the mutex case, there is
no way to re-initialize a pthread_once_t object.
I don't know... The wording in the spec does not prohibit
re-initialization of a variable. It is only necessary that it's a
non-automatic variable. Even with PTHREAD_ONCE_INIT is a data structure
you can initialize a variable with

once = (pthread_once_t) PTHREAD_ONCE_INIT;

That's not possible before C99 but we assume C99 now.


In general object unloading is something that's not portable, as the
weaselly wording in dlclose() shows. I don't think any of the points
you bring up are really a problem. Just don't use dlclose(). And if
you do, rely on implementation-specific extensions.

- --
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
Loading...