[cpp-threads] Asynchronous Execution Issues (caching_async)

Hans Boehm Hans.Boehm at hp.com
Wed Apr 29 05:12:02 BST 2009



On Tue, 28 Apr 2009, Peter Dimov wrote:

> Boehm, Hans:
>> The more I think about these issues, the less comfortable I get with
>> anything like caching_async.  Given the lifetime issues we've already been
>> running into, the fact that caching_async essentially delays destruction
>> of thread_local objects far beyond the context in which they were created
>> seems really dangerous.
>>
>> If I write a function that implements a parallel algorithm, I generally
>> have no way of knowing whether any iterators I'm passed refer to local
>> (automatic) objects or heap objects of limited lifetime.  But if I create
>> a thread_local reference to them, I run the risk of accidentally accessing
>> them long past completion of the requested task, in the destructor for the
>> thread_local, with exceptionally disastrous consequences.  I don't know
>> how easy it is to make such a mistake, but I can't convince myself that it
>> would be rare.  In any case, this seems like way too dangerous a feature
>> to introduce at this stage without any experience to back it up.
>
> Not to downplay the problem, but this is not a new issue. Creating a
> dangling thread_local reference is conceptually not very different from
> creating a dangling global reference in the single-threaded case.
> Furthermore, this mistake is just as applicable to a "normal" async.
> Thread-local (and global, in the ST case) state is used when the object
> needs to persist beyond a single function call. If a function using TLS has
> a lifetime bug, this will affect both async variants (in subtly different
> ways). It will even affect a synchronous "async".
>

What scares me about the thread_local case here is that we're artificially 
and somewhat surprisingly extending the lifetime of some objects. Consider 
calling a function par_func(y) that internally invokes caching_async(), 
which in turn runs a function that caches a copy x of some piece of y in a 
thread_local.  If y and hence x happen to use, for example, an allocator 
whose lifetime is limited to that of a caller to par_func(y), we 
potentially end up with an asynchronous stack smash for the calling 
thread.

This both seems considerably more surprising to me than the 
single-threaded case, and considerably harder to track down when it does 
happen.  At a minimum, I think we would have to be explicit about whether
library functions might keep copies of input data in thread_locals.

It seems to me that Cilk, TBB et al. are also likely to run into this 
issue. The reason they haven't yet may just be that we currently tend to 
have poor support for destruction of __thread variables and the like, and 
that these systems so far have somewhat limited use??

Hans



More information about the cpp-threads mailing list