[cpp-threads] N2880 and thread_local variables
Anthony Williams
anthony at justsoftwaresolutions.co.uk
Tue Jun 9 08:32:14 BST 2009
Beman Dawes wrote:
> On Mon, Jun 8, 2009 at 6:15 PM, Anthony
> Williams<anthony at justsoftwaresolutions.co.uk> wrote:
>> Hi,
>>
>> I have been thinking about the issue of thread_local variables with
>> destructors and lifetime that is discussed in N2880, and would like to
>> suggest a possible solution.
>>
>> My suggestion is this: create a new class thread_local_context which is
>> intended to be used in an RAII manner. Creating an instance creates a new
>> context on the calling thread for thread_local variables to be associated
>> with. When the object is destroyed it destroys all thread_local variables
>> modified on this thread since it was created.
>>
>> Destroying such an object from a thread other than that in which it was
>> created would be an error, as would destroying such objects in any order
>> other than the reverse of their construction within a single thread.
>>
>> Nesting such objects would create a new (blank) context for thread_local
>> variables, and the outer context would be restored when the nested object
>> was destroyed.
>
>> Alternatively, nested contexts could inherit the set of
>> thread_local variables from their parent context, and just destroy any
>> newly-set values when the nested context is destroyed.
>
> Without claiming to have done any deep analysis, wouldn't this
> alternate behavior be less surprising in that it is very similar to
> familiar name viability rules for blocks?
Yes. My concern is just that lookup of thread_local variables now needs
to chain across contexts --- if the variable is not in the current
context, the compiler will need to check the parent context, and maybe
its parent, and so on. If each context is a clean slate then the
compiler does not need to do this chaining. I suppose the
compiler/runtime could move this cost to the construction of a context,
by importing the variables directly, but marking them as "in parent
context".
> Also, wouldn't this alternate behavior avoid the problem that Peter
> pointed out? Presumably the heap would have been set up in the
> outermost context, rather than having to be setup and then torn down
> as each nested context comes into existence and then dies.
Here is Peter's example:
> As a counterpoint, however, consider this task:
>
> void task()
> {
> delete new int;
> }
>
> and assume that the implementation of operator new uses thread_local
> variables to maintain a per-thread free list.
Hmm. I see the problem here. Obviously, the runtime could optimize
operator new so this wasn't a problem, whatever the actual behaviour of
thread_local and thread_local_context.
However, there are a class of uses of thread-local objects where
expensive-to-construct objects are cached in thread-local storage for
the life of a thread, and similar concerns could be said to apply to them.
That said, I think it is *precisely* these expensive-to-construct and
therefore probably heavy-resource-using objects, probably also with
non-trivial destructors, that N2880 is concerned about.
I think it is therefore desirable that such objects are cleared down
when a thread_local_context is destroyed. It allows a thread to be
reused for unrelated tasks without accumulating expensive thread-local
objects (one of the problems outlined in N2880), and it ensures that the
non-trivial destructor for the object is run before a waiting thread is
notified that the thread running the task has completed (assuming the
notification is outside the scope of the thread_local_context).
By choosing to have a context inherit the variables from its parent
context, you could deliberately set up a thread that would create such a
thread-local variable and then run multiple discrete tasks inside their
own nested contexts, all of which would inherit this "expensive"
variable, but any additional thread-locals would be cleared down at the
end of each task. As Beman pointed out, this would provide a way to
address this particular issue.
I don't expect that thread_local_context objects would be widespread ---
their use would likely be limited to the implementation of thread pools
and task-scheduling frameworks such as a potential std::async and
std::packaged_task.
Anthony
--
Author of C++ Concurrency in Action | http://www.manning.com/williams
just::thread C++0x thread library | http://www.stdthread.co.uk
Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
More information about the cpp-threads
mailing list