Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [day] [month] [year] [list]
Date: Wed, 04 Mar 2020 14:39:16 +0100
From: Florian Weimer <fweimer@...hat.com>
To: libc-coord@...ts.openwall.com
Subject: Destructors for (ELF) TLS variables

POSIX supports destructors for its per-thread data support.  The
behavior is slightly different from what has been standardized for C++
thread_local variables with destructors:

| If, after all the destructors have been called for all non-NULL values
| with associated destructors, there are still some non-NULL values with
| associated destructors, then the process is repeated. If, after at
| least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor calls
| for outstanding non-NULL values, there are still some non-NULL values
| with associated destructors, implementations may stop calling
| destructors, or they may continue calling destructors until no
| non-NULL values with associated destructors exist, even though this
| might result in an infinite loop.

This iteration behavior might look like a crass hack, but I think it is
actually a fairly reasonable way to deal with the destruction problem
for singletons.  (If I recall correctly, Andrei Alexandrescu discusses
the surrounding issues extensively in Modern C++ Design, in the context
of general, non-thread-specific singletons, which face exactly the same
problem on process termination.)

In the GNU toolchain, we currently do not have support for TLS
destructors, and the C++ support we have slightly broken (it crashes on
out-of-memory conditions).  I wonder if it would make sense to define
something analogous to DT_FINI_ARRAY, but for destructors that are
called on thread exit.

Has anyone implemented something like that?  It does not look too hard
to do.  The required changes should be largely mechanical (new ELF
section header type etc.).

Would it make sense to mirror the POSIX iteration behavior?  We probably
cannot check if the relevant TLS variables are zero, so we'd likely have
to rely on a return value from the destructor to indicate if it has
performed any actions that might have resurrected further thread-local
state.

Integration with dlclose is another open question.  It's possible to do
nothing special here and leak resources (because the destructors for
running threads cannot be called once their code is gone).  If that's
not feasible for some DSOs, they would have to register the thread-local
state with the DSO and perform the dealloction (with proper
synchronization) from the DSO's destructor.  Or they can specify
NODELETE to prevent unloading.

I'm thinking about this because we are considering the removalof
deferred resource allocation for ELF TLS variables.  If we ever do that,
memory would be allocated on thread creation or during dlopen, and not
on first access to the variable.  But it would mean that libraries may
want to add an indirection if they need large, occasionally used
per-thread data.  But then they need some sort of destructor to
deallocate that storage.

(The main advantage of ELF TLS over pthread_key_create etc. is better
integration with tools like debuggers, and increased type safety at the
language layer.)

Thanks,
Florian

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.