[cpp-threads] Asynchronous Function Proposal

Lawrence Crowl Lawrence at Crowl.org
Tue Jun 16 01:08:52 BST 2009


On 6/13/09, Herb Sutter <hsutter at microsoft.com> wrote:
> I've now looked at the proposal further. It has implemented some
> of the changes suggested in the earlier thread, but still hasn't
> implemented some that I consider essential. In particular (and
> I don't think I was the only person to argue for these):
>
>
>   - The async policy parameter is still first, and we don't need
>   a variadic overload. There should be just one async() with a
>   defaulted policy parameter.

I have no objection to this approach a-priori, but I think the
async parameters should be consistent with the thread constructor
parameters.  If thread changes, the async should change as well.

>   - The wording for the first policy still says "in a new thread,"
>   which prevents the implementation from caching threads in any way
>   (including the highly desirable option of running on a thread
>   pool). It should say "in another thread."

N2880 says why allowing thread caching is not feasible.  We can do
caching when we have thread pools, but I don't believe that we can
do so until then.

BTW, the OS can still cache threads underneath, but it still has
to destroy and reconstruct the thread-local variables.

>   - There is still a new future type, async_future. It should be
>   unique_future.  (In related news, the return type of async()
>   should be convertible to shared_future. Adding a conversion
>   from async_future to shared_future would satisfy that part,
>   but not satisfy the main point in this bullet.)

Can you provide a rationale based on use cases?

> As I mentioned to Lawrence privately last week, I strongly prefer
> to avoid having competing proposals for Frankfurt, but I feel
> strongly enough about these issues that if we feel we don't have
> consensus to word this proposal this way, I'm willing to write
> a competing proposal to make sure a version that looks like the
> above appears in an on-time paper for consideration in Frankfurt.

I am quite happy to have one paper present alternate technical
solutions, if the one in the paper is not what you want.  However,
both technical solutions need rationale.

> Less importantly, the policy names are awkward. I'd prefer names
> more like "elsewhere/here/either" or "async/sync/either" or
> "this_thread/another_thread/either".

The names are likely to change to reflect semantic details.

> Also, whether we diverge proposals or not, I would like to see
> this paragraph reworded:
>
> > The proposed solution consists of a set of async functions
> > to launch asychronous work and a new kind of future to
> > manage the function result.  This solution derives from
> > an extensive discussion on the C++ threads standardisation
> > <cpp-threads at decadentplace.org.uk> mailing list.
>
> That wrongly implies that the discussion blessed/suggested both the
> "set of async functions" and "a new kind of future" design points
> -- both of which I and others have objected to. At minimum this
> needs to acknowledge that there were strong objections against
> both of these features.

Note that I said discussion, not consensus.  It was my intent to
capture the discussion and choices in the document.  If you feel
that I have not done so accurately, please send me text.

> > The model for this feature, as I understand it, is to provide
> > a means for people with a sequential program to easily upgrade
> > it to exploit a small number of cores.
>
> No, it's simply to provide a simple means to perform an async
> function call that yield an async result, that is instead of
>
>   T t = f();
>
> to write (I'll say just "future" to avoid the argument about the
> exact type here)
>
>   future<T> t = async( []{ f(); } );
>
> without writing packaged_tasks and having to fill futures/promises
> manually.

Unfortunately, packaged tasks and futures are only part of the
specification.  The issues presented in N2880 are handled in the
code around those primitives.  That is, by providing a set of very
primitive mechanisms, packaged task et al punt on the hard issues.
By introducing async, we are squeezing out that code, and hence
need to address those issues.

>
> Beyond that basic requirement, there's also the desire (which the
> proposal already supports, thanks!) to allow, but not require,
> implementations that run the task on a thread pool or a work
> stealing runtime. To enable all of that, it's sufficient to have
> the three options "on this thread," "on a different thread,"
> or "either." The latter two enable thread pools; the last alone
> enables work stealing.

Unfortunately, the latter two do not enable thread pools because
of the problems outlined in N2880.  Use of a thread pool, without
exposing the pool object to programmers, is dangerous.

When I said "new thread", I meant a new thread with new TLS lifetimes
because it solves the problems in N2880.

>
>   Policy              new thread    thread pool     work stealing
>
>   on this thread         no            no               no
>   on another thread      yes           yes              no
>   either                 yes           yes              yes

Furthermore, any implementation that permits thread pools will
necessarily restrict the kinds of tasks that can be programmed,
because otherwise the pool synchronization interacting with
inter-task synchronization may introduce deadlock.

> > Switching to unique_future can be done relatively easily at
> > Frankfurt.
>
> No, I want there to be a concrete on-time proposal on the table
> in Frankfurt whose basic parts look correct to me, including
> that it does not invent some new future type and the other major
> points above.

The proposal is concrete, on-time, and looks correct to me.  I am,
however, realistic enough to know that whatever we have ready will
be modified by the committee.

> I wasn't the only one who argued against having yet another
> future type only for use with async. I want async() to return a
> unique_future and I am happy with requiring the implementer to
> make it work. (Yes, it would be better still if unique_future
> and shared_future more easily let us accomplish what we want,
> but that's not necessary to have async() return the correct
> type, which I believe is unique_future as long as that remains
> convertible to shared_future.)

I do not particularly like having to propose a new future type.
Nor do I like the consequences of trying to reuse the existing
future types.  If you can present an implementation that avoids
the consequences, I am quite happy to change the async return type.

> > Creating an async_future proposal at Frankfurt would not be easy.
> > So, in the interests of leaving our options open,
>
> It's not an option I want to see open. ;-) We have a future
> type; we should use it. I do not think this is a case where it's
> desirable to keep options open, because inventing a new one-off
> future type is just undesirable.

But requiring a massive increase in the implementation complexity
of the existing futures is also undesireable.  It also violates
the principle of "pay for what you use".

> If you want to keep it in this proposal that's fine, but I
> disagree and I need there to be some proposal for Frankfurt that
> uses unique_future.
>
> > I addressed the choice in the paper, and I'll happily expand
> > that section to meet your concerns.
>
> I appreciate that, and I'd be okay with the proposal introduction
> mentioning this discussion and the pros/cons of using a
> separate future type -- as long as the proposed text itself uses
> unique_future and doesn't invent a new future type.
>
> Are you open to doing a revision along those lines, as well as
> the other points at top? I understand if you're not and either
> way I do appreciate the work you've put into this and the very
> useful discussion it has made possible! Thanks Lawrence,

I am open to a revision, but not without new information.
In particular, I need convincing text on:

   Why the parameters to async should be different from the thread
   constructor.  (If the thread constructor parameters change, this
   point becomes moot.)

   Why the issues presented in N2880 are not a problem using the
   existing unique_future.

   Why the performance and implementation costs of any fixes to
   the above are acceptable.

At present, I am happier with the proposal as it stands than with
the suggestions that you have made.  While I like benefits of your
suggestions, I dislike their consequences more.  If you can change
the consequences, I will be even happer to change the proposal.

-- 
Lawrence Crowl



More information about the cpp-threads mailing list