[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