[cpp-threads] Asynchronous Execution Issues (caching_async)

Boehm, Hans hans.boehm at hp.com
Wed Apr 29 20:13:07 BST 2009


 

> -----Original Message-----
> From: cpp-threads-bounces at decadentplace.org.uk 
> [mailto:cpp-threads-bounces at decadentplace.org.uk] On Behalf 
> Of Peter Dimov
> Sent: Wednesday, April 29, 2009 6:31 AM
> To: C++ threads standardisation
> Subject: Re: [cpp-threads] Asynchronous Execution Issues 
> (caching_async)
> 
> Hans Boehm:
> 
> > 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.
> 
> I'd say that in general, objects that can outlive their 
> storage (i.e. the memory can be reclaimed before the 
> destructor runs) are inherently dangerous and passing them to 
> unknown third-party code is a gamble either way, even with 
> absolutely no thread_local support; these objects violate 
> C++'s general lifetime principles in a fundamental way. A 
> refcount and an assert in the allocator destructor seems not 
> a bad idea. :-)
I personally increasingly agree with that.  However, we just added all this support for allocator instances to the language, which seems to be designed precisely to support this "dangerous" practice.  It seems to me that adding all this support for objects that can't safely be passed to third-party libraries is at least confusing.

To be fair, I really do suspect that allocators are only an example of that, though probably the most natural one.  I would expect that there are other instances in which some objects P live only through a particular program phase, and are regularly referenced by tasks launched during that phase.  At the end of this phase, objects P go away, but arbitrarily delayed destruction of objects from that phase would reference them beyond their lifetime.

> 
> This aside, I agree that the thread_local case works (if join 
> is guaranteed to wait) with a dedicated thread, and breaks 
> with a thread pool. But note that the global case always 
> breaks. It is not uncommon for functions to keep cached 
> elements not in a thread local, but in a global data structure.
Good point.  But static destructors already seem to have a bad reputation for reasons like this.  And we're at best adding another version of the problem with arguably worse symptoms, in that the crashes don't just happen at process shutdown, when they tend to get ignored.

> 
> Placing the blame here is obviously a matter of opinion, but 
> I'd be inclined to say that having to use a dedicated thread 
> for a specific task - for lifetime reasons alone - means that 
> the design is broken. 
I guess my concern here is that we've managed to get a combination of features in the language (destructors for thread_locals and statics, detached threads, allocator instances, ...) that really don't play together very well.  And the fact that they don't is too subtle.  Lawrence's exercise has pointed this out.  Caching_async makes things worse, but the fact that anything along those lines is so hard to get right worries me more.  I think we're unintentionally planting too many traps here.

I'm not sure what the best way is to fix this.  In fact I'm not even sure whether this should be a core or library issue.  But I do think it needs to get discussed.  At a minimum, I think we need a bunch of notes pointing out the traps.  And ideally they should contain usage recommendations that avoid the traps.  And if we want to preserve thread_local destructors (I can see strong arguments for and against that), I'm starting to like the "detroy_thread_locals_now()" API call.  An implementation could presumably arrange for it to be a fast no-op if none were constructed.

Hans
> 
> 
> --
> cpp-threads mailing list
> cpp-threads at decadentplace.org.uk
> http://www.decadentplace.org.uk/cgi-bin/mailman/listinfo/cpp-threads
> 


More information about the cpp-threads mailing list