Topic: Concerns regarding N3564 (Resumable Functions)


Author: Michael Bruck <bruck.michael@gmail.com>
Date: Wed, 10 Apr 2013 23:17:45 +0200
Raw View
N3564 Resumable Functions
N3558 A Standardized Representation of Asynchronous Operations


1. The N3564 proposal gives a lot of freedom to the compiler in
implementing the feature (examples in section 3.2 and 3.3 of the
paper). For portable libraries code performance of some algorithms
will become unpredictable. This may limit adoption of this feature.

More specifically: In a very simplistic implementation the heap based
approach in 3.2 may lead to very short times for entering a resumable
function at the expense of slower returns from deeply nested
suspension points. A stack based approach may take longer and/or use
much more memory to set up the stack for the first resumable function
but may be significantly faster when using context switches to return
from deeply nested suspension points.

2. AFAICT N3564 provides no means to specify a custom allocator or at
least provide a hook for a replaceable global allocator for a heap
based implementation. Some implementations will want to provide custom
allocators to prioritize these calls over other heap usages when
memory is scarce because they are required just to execute code. Also
some generic heap may not be the most efficient way to provide these
frames. Also it is not clear how out of memory situations are handled.

3. What is the approach to cancel and dispose of pending resumable
functions? This needs some sort of best practices guidance, examples
or at least a discussion what problems may arise from this. It should
be possible to safely cancel and dispose of such functions in a
generic manner.

4. Permitting new OS dependencies for example in implementations that
need to obtain dynamic stacks could hamper adoption. If a popular
cross-compiler platform chooses such a path and leaves the details to
some platform library this could lead to low usage for many small
embedded platforms for which this facility is difficult or impossible
to implement. Recommending or mandating a OS independent
implementation would avoid this problem.

5. There were some misunderstandings in discussions on this list about
this paper. It may be a good idea to be more explicit in 2.1 that the
programmer has control over how the function is resumed and that
having it handled by =93scheduling logic of the runtime=94 is merely one
option.

Michael

--=20

---=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/?hl=3Den.



.


Author: sean.middleditch@gmail.com
Date: Sun, 21 Apr 2013 14:27:44 -0700 (PDT)
Raw View
------=_Part_1489_18481301.1366579665082
Content-Type: text/plain; charset=ISO-8859-1

On Wednesday, April 10, 2013 2:17:45 PM UTC-7, Michael Bruck wrote:
>
> 3564 Resumable Functions
> N3558 A Standardized Representation of Asynchronous Operations
>


> 2. AFAICT N3564 provides no means to specify a custom allocator or at
> least provide a hook for a replaceable global allocator for a heap
> based implementation. Some implementations will want to provide custom
> allocators to prioritize these calls over other heap usages when
> memory is scarce because they are required just to execute code. Also
> some generic heap may not be the most efficient way to provide these
> frames. Also it is not clear how out of memory situations are handled.


Note that concern 2 listed by Michael is a complete showstopper for some
C++ users.

In general, I think C++ should avoid mandating the use of specific
policy-deciding library types for language features.  Purely mechanism
types like many of the compiler-assisted type traits are one thing, but
when the choice of type affects how the program behaves, the user _needs_
to be able to supply their own custom types when using any language
feature.  The ability to do that is one of the defining characteristics of
C++ compared to many other high-level languages and is of great import to
some market segments.  At a sub-committee meeting last June it was
suggested to me that developers making those kinds of applications could
just not use new C++ features that didn't allow the required level of
control, but that seems antithetical to C++'s nature and is a sub-optimal
resolution given how solvable these problems usually are during the design
phase.

There are a few possible solutions I see to this specific problem.  Avoid
specifying resumable functions in terms of std::future and instead specify
them in terms of a Future concept, allowing any conforming user-defined
type to be considered an acceptable return type for resumable function.
 Then the "frame holder" (std::shared in the paper) should also be
replaceable with any type that conforms to something like a SharedPointer
concept.  The next piece would be to allow some (optional) user-provided
parameters, including the type to use, when constructing the frame when
using the await keyword.

Just to get the idea ball rolling, the following is a possible syntax:

f = await expr;  // uses std::shared to hold the frame, common case, as
proposed
f = await<my_type>{ param1, param2 } expr; // uses my_type and additional
parameters in place of std::shared
f = await<my_type<type1, type2>>{ param1, param2 } expr; // example with
my_type additional template args

f = await<shared_ptr_maker> expr; // identical to first example



I believe this syntax is both backwards-compatible, unambiguous, and rather
obvious, if a little ugly.  I think
the syntax bike-shedding is a bit less important than figuring out correct
semantics right now, though.

The supplied type is expected to conform to its own Frame Holder concept
which is responsible for constructing and maintaining the lifetime of the
compiler-generate frame object, the type of which could be passed as an
additional automatic template parameter, such that the simplified final
generate code might be something like:

struct __frame { /* compiler-generated frame members */ };

auto __awaited = my_type<__frame, type1, type2>{ __frame{ /* captured
locals */ }, param1, param2 };
expr.then( [__awaited]( decltype(expr) f ){ __awaited->f = f.get(); /*
continuation body */ } );


In the example, my_type is responsible for allocating storage for the
continuation functor.  It might just do it with global new and delete, as
the standard type would no doubt do.  This replaces the used of std::shared
in the original proposal.  This is a bit different than the current paper
as it uses make_shared to construct the frame type as shared_ptr itself
does no such construction.  This might be resolvable by using a new
shared_ptr_maker type that move-converts to shared_ptr or modifying the
above syntax to work in terms of maker functions rather than types, e.g.

f = await expr; // standard

f = await<::std::make_shared> expr; // equivalent to standard

f = await<make_my_handle<Heap*>>{ &heap } expr; // full syntax possible


With the generated code for the last line then being equivalent to the
simplified:

struct __frame { /* ... */ };

auto __awaited = make_my_handle<__frame, Heap*>( __frame{}, &heap );

expr.then([__awaited]( decltype(expr) f ){ __awaited->f = f.get(); /* ...
*/ });


Note that using move semantics for __awaited makes more sense; the code is
purely expositional so I'm ignoring that bit here.

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.



------=_Part_1489_18481301.1366579665082
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

<div>On Wednesday, April 10, 2013 2:17:45 PM UTC-7, Michael Bruck wrote:<bl=
ockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px 0.8ex; border-l=
eft-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: s=
olid; padding-left: 1ex;">3564 Resumable Functions&nbsp;<br>N3558 A Standar=
dized Representation of Asynchronous Operations&nbsp;<br></blockquote><div>=
&nbsp;</div><blockquote class=3D"gmail_quote" style=3D"margin: 0px 0px 0px =
0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); borde=
r-left-style: solid; padding-left: 1ex;">2. AFAICT N3564 provides no means =
to specify a custom allocator or at&nbsp;<br>least provide a hook for a rep=
laceable global allocator for a heap&nbsp;<br>based implementation. Some im=
plementations will want to provide custom&nbsp;<br>allocators to prioritize=
 these calls over other heap usages when&nbsp;<br>memory is scarce because =
they are required just to execute code. Also&nbsp;<br>some generic heap may=
 not be the most efficient way to provide these&nbsp;<br>frames. Also it is=
 not clear how out of memory situations are handled.&nbsp;</blockquote></di=
v><div><br></div>Note that concern 2 listed by Michael is a complete showst=
opper for some C++ users.<div><br></div><div>In general, I think C++ should=
 avoid mandating the use of specific policy-deciding library types for lang=
uage features. &nbsp;Purely mechanism types like many of the compiler-assis=
ted type traits are one thing, but when the choice of type affects how the =
program behaves, the user _needs_ to be able to supply their own custom typ=
es when using any language feature. &nbsp;The ability to do that is one of =
the defining characteristics of C++ compared to many other high-level langu=
ages and is of great import to some market segments. &nbsp;At a sub-committ=
ee meeting last June it was suggested to me that developers making those ki=
nds of applications could just not use new C++ features that didn't allow t=
he required level of control, but that seems antithetical to C++'s nature a=
nd is a sub-optimal resolution given how solvable these problems usually ar=
e during the design phase.</div><div><br></div><div>There are a few possibl=
e solutions I see to this specific problem. &nbsp;Avoid specifying resumabl=
e functions in terms of std::future and instead specify them in terms of a =
Future concept, allowing any conforming user-defined type to be considered =
an acceptable return type for resumable function. &nbsp;Then the "frame hol=
der" (std::shared in the paper) should also be replaceable with any type th=
at conforms to something like a SharedPointer concept. &nbsp;The next piece=
 would be to allow some (optional) user-provided parameters, including the =
type to use, when constructing the frame when using the await keyword.</div=
><div><br></div><div>Just to get the idea ball rolling, the following is a =
possible syntax:</div><div><br></div><blockquote style=3D"margin: 0 0 0 40p=
x; border: none; padding: 0px;"><div><font face=3D"courier new, monospace">=
f =3D await expr; &nbsp;// uses std::shared to hold the frame, common case,=
 as proposed</font></div><div><font face=3D"courier new, monospace">f =3D a=
wait&lt;my_type&gt;{ param1, param2 } expr; // uses my_type and additional =
parameters in place of std::shared</font></div><div><font face=3D"courier n=
ew, monospace">f =3D await&lt;my_type&lt;type1, type2&gt;&gt;{ param1, para=
m2 } expr; // example with my_type additional template args</font></div></b=
lockquote><blockquote style=3D"margin: 0 0 0 40px; border: none; padding: 0=
px;"><div><font face=3D"courier new, monospace">f =3D await&lt;shared_ptr_m=
aker&gt; expr; // identical to first example</font></div></blockquote><div>=
&nbsp;</div><div><br></div><div>I believe this syntax is both backwards-com=
patible, unambiguous, and rather obvious, if a little ugly. &nbsp;I think</=
div><div>the syntax bike-shedding is a bit less important than figuring out=
 correct semantics right now, though.</div><div><br></div><div>The supplied=
 type is expected to conform to its own Frame Holder concept which is respo=
nsible for constructing and maintaining the lifetime of the compiler-genera=
te frame object, the type of which could be passed as an additional automat=
ic template parameter, such that the simplified final generate code might b=
e something like:</div><div><div><br></div></div><blockquote style=3D"margi=
n: 0 0 0 40px; border: none; padding: 0px;"><div><div><font face=3D"courier=
 new, monospace">struct __frame { /* compiler-generated frame members */ };=
</font>&nbsp;</div></div></blockquote><div><blockquote style=3D"margin: 0 0=
 0 40px; border: none; padding: 0px;"><div><font face=3D"courier new, monos=
pace">auto __awaited =3D my_type&lt;__frame, type1, type2&gt;{&nbsp;</font>=
<span style=3D"font-family: 'courier new', monospace;">__frame{ /* captured=
 locals */ },&nbsp;</span><span style=3D"font-family: 'courier new', monosp=
ace;">param1, param2 };</span></div><div><font face=3D"courier new, monospa=
ce">expr.then( [__awaited]( decltype(expr) f ){ __awaited-&gt;f =3D f.get()=
; /* continuation body */ } );</font></div></blockquote><div><br></div><div=
>In the example, my_type is responsible for allocating storage for the cont=
inuation functor. &nbsp;It might just do it with global new and delete, as =
the standard type would no doubt do. &nbsp;This replaces the used of std::s=
hared in the original proposal. &nbsp;This is a bit different than the curr=
ent paper as it uses make_shared to construct the frame type as shared_ptr =
itself does no such construction. &nbsp;This might be resolvable by using a=
 new shared_ptr_maker type that move-converts to shared_ptr or modifying th=
e above syntax to work in terms of maker functions rather than types, e.g.<=
br></div></div><div><br></div><div><blockquote style=3D"margin: 0 0 0 40px;=
 border: none; padding: 0px;"><div><font face=3D"courier new, monospace">f =
=3D await expr; // standard</font></div></blockquote></div><blockquote styl=
e=3D"margin: 0 0 0 40px; border: none; padding: 0px;"><div><div><font face=
=3D"courier new, monospace">f =3D await&lt;::std::make_shared&gt; expr; // =
equivalent to standard</font></div></div></blockquote><blockquote style=3D"=
margin: 0 0 0 40px; border: none; padding: 0px;"><div><font face=3D"courier=
 new, monospace">f =3D await&lt;make_my_handle&lt;Heap*&gt;&gt;{ &amp;heap =
} expr; // full syntax possible</font></div></blockquote><div><br></div><di=
v>With the generated code for the last line then being equivalent to the si=
mplified:</div><div><br></div><blockquote style=3D"margin: 0 0 0 40px; bord=
er: none; padding: 0px;"><div><font face=3D"courier new, monospace">struct =
__frame { /* ... */ };</font></div></blockquote><blockquote style=3D"margin=
: 0 0 0 40px; border: none; padding: 0px;"><div><font face=3D"courier new, =
monospace">auto __awaited =3D make_my_handle&lt;__frame, Heap*&gt;( __frame=
{}, &amp;heap );</font></div></blockquote><blockquote style=3D"margin: 0 0 =
0 40px; border: none; padding: 0px;"><div><font face=3D"courier new, monosp=
ace">expr.then([__awaited]( decltype(expr) f ){ __awaited-&gt;f =3D f.get()=
; /* ... */ });</font></div></blockquote><div><br></div><div><div>Note that=
 using move semantics for __awaited makes more sense; the code is purely ex=
positional so I'm ignoring that bit here.</div></div>

<p></p>

-- <br />
&nbsp;<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
&nbsp;<br />
&nbsp;<br />

------=_Part_1489_18481301.1366579665082--

.