[cpp-threads] Asynchronous Function Proposal

Herb Sutter hsutter at microsoft.com
Mon Jun 8 16:02:47 BST 2009


Hans wrote:
> It sounds to me like Lawrence's proposal fundamentally would work, with
> one highly desirable change:  We do need to give the implementation the
> necessary leeway to run an async task at the point of call, so that it
> can throttle the number of async tasks when it becomes necessary for
> space reasons.  

Let me argue against myself on this one, because until recently I had more sympathy for this.

I'd had a little warm place in my heart for the interpretation "if you pick the synchronous policy the code has the same effect as if you didn't write async()." But I'm now convinced I was wrong to want that to be explicit, in part because we get it anyway. One major reason is consistent error handling (see below). A second is that, if there are no synchronization operations between the async() statement and the join point, it doesn't matter anyway because as-if + optimizations let us get the same effect anyway.

I do think Lawrence's proposal needs to change in ways we said before (Lawrence, are you planning to circulate an update), for example to replace "in a new thread" with "on another thread" and reduce the overloads to one or two with the policy last. But I don't think this requires a change to the three policies he proposed; modulo naming (I'd pick different names) he has the right three options of "run on another thread," "run on this thread" (at the join point), and "implementation gets to pick." The last two allow that leeway already, don't they, especially if there are no synchronization operations between the async() and the join?


> Possibly this could be another policy option, 

Let me just chime in again that I think the three existing policies are enough. :-)


> It's unclear to me whether that means that exceptions should sometimes
> become visible at the point of async creation or not.  

Please no. Lawrence's paper is definitely right about avoiding EH at two places. We definitely must say that there is a single well-defined place where the exception is thrown. It's already hard enough to write error-handling code, and already done insufficiently consistently in practice by people. We can't ask people to for evermore double their work by writing code that handles an exception in both of two possible places just because we ducked taking a decision on where the right place is.

> Even if exceptions are handled only at the request point, we need to
> explicitly allow running the task earlier, since that's visible by
> other means.

What other means? By definition, the task should be independent of the caller, including that it shouldn't take the same locks the caller might take, so it's unclear to me how its side effects could become visible before the join in a correct program. And with as-if + optimizations, we can already execute the task earlier than the join if there are no barriers in between.

> I would still favor sticking with Lawrence's design that avoids
> (visibly) reusing threads for multiple tasks.  The issues with thread
> locals seem to me to be far more serious for C++ than Java.  On the
> other hand, it would be good to set things up so that we can easily add
> more policies later.  (I'm assuming that thread locals with arbitrary
> destructors are here to stay, though I think we now have strong
> arguments on both sides of that issue.)
> 
> A more minimalist approach may be to support only the fully_threaded
> policy for now, but leave room for additional policies in the future.

I disagree. We should provide "run on another thread" (not necessarily a new thread!), "run on this thread," and "implementation decides."

Herb




More information about the cpp-threads mailing list