1. You say that this is a library only change but you require that it has a stack that can grow on demand. As far as I know that can not currently be implemented in C++. So that would not make this a library only change, right? I think the requirement for growing stacks is a good one and we should not drop it just to make this a library only change. It might be a good idea to make the growing stack a separate class that is used by the coroutine.
2. Why does the coroutine get called from the constructor? You say that otherwise it is difficult to detect the completeness of a coroutine, but I don't understand why that would be. Can you explain the std::distance example you gave and why it wouldn't work if you created the coroutine in one line and called operator() in the next?
3. Why does operator() return the coroutine itself? I could understand if it returns the result (or a std::optional<result>) or void. But I don't see why it would return the coroutine itself. I think it should return void, because anything else is confusing. For example this will compile with boosts coroutine implementation:
coroutine<bool ()> coro(/*...*/);
bool result = coro(); // doesn't actually assign the result of the coroutine, but the current state instead
4. How does the coroutine unwind the stack on destruction? I believe that in boost it's done by throwing an exception, which unwinds the stack from the point where you last left the coroutine to where the exception is being caught.
Meaning if nobody catches that exception the stack is unwound all the way, but that fails if there is a catch(...) block anywhere that doesn't re-throw.
I think it's a reasonable requirement to say that nobody must have a catch(...) block that doesn't re-throw if you want stack unwinding to work, but if the standard is to be changed, then this could be made more robust.
Am Samstag, 16. März 2013 18:43:28 UTC+1 schrieb maltes...@gmail.com:1. You say that this is a library only change but you require that it has a stack that can grow on demand. As far as I know that can not currently be implemented in C++. So that would not make this a library only change, right? I think the requirement for growing stacks is a good one and we should not drop it just to make this a library only change. It might be a good idea to make the growing stack a separate class that is used by the coroutine.
On demand growing stacks can be used by C++ - I've already done it for boost.coroutine (>=1.54 - currently in boost-trunk). But this requires support by the compiler. As I wrote in the proposal I know only GCC (>=4.7) to supports segmented/split stacks.
(OK - llvm has also segmented stack but the clang front end does not allow to use it, at least not yet).
So, today we have already on demand growing stacks (you could try boost.coroutine from boost-trunk -> 'b2 toolset=gcc segmented-stack=on'). I think the user should not deal with stack allocation etc. - this should be done inside coroutine<>.
2. Why does the coroutine get called from the constructor? You say that otherwise it is difficult to detect the completeness of a coroutine, but I don't understand why that would be. Can you explain the std::distance example you gave and why it wouldn't work if you created the coroutine in one line and called operator() in the next?
If we enter the coro-fn no at the coroutine<>-ctor you are forced to call coroutine::operator() (which does the jump into coro-fn) 4-times. The 4th time is required because after the 3thd jump from the coro-fn we still don't know in the main-thread/task (caller don't now hat would be the best wording) if the coro-fn would return another return value or would it be terminate (== return from coro-fn body).
In contrast to this - if we enter coro-fn with coroutine<>-ctor - we know after the 3rd jump from coro-fn that the coro-.fn terminated.
3. Why does operator() return the coroutine itself? I could understand if it returns the result (or a std::optional<result>) or void. But I don't see why it would return the coroutine itself. I think it should return void, because anything else is confusing. For example this will compile with boosts coroutine implementation:
in order to check if the coro-fn has returned a result or termiantedcoroutine<bool ()> coro(/*...*/);
bool result = coro(); // doesn't actually assign the result of the coroutine, but the current state instead
result would tell you if coro is still valid (== coroutine<>::operator() can be called) or if coro-fn has teminated (== you must nor call coroutine<>::operator())
4. How does the coroutine unwind the stack on destruction? I believe that in boost it's done by throwing an exception, which unwinds the stack from the point where you last left the coroutine to where the exception is being caught.
yes - that is how boost.coroutine unwinds the stack (most generic one because not all compiler implement __unwind API)
Meaning if nobody catches that exception the stack is unwound all the way, but that fails if there is a catch(...) block anywhere that doesn't re-throw.
no - boost.coroutine uses a trampoline function which catches the unwind-exception
I think it's a reasonable requirement to say that nobody must have a catch(...) block that doesn't re-throw if you want stack unwinding to work, but if the standard is to be changed, then this could be made more robust.
I would expect that the compiler implementer calls its compiler specific stack unwinding API so that throwing and catching a special unwind-exception isn't necessary
If it requires support by the compiler then this is hardly a library only change. I agree that the feature is needed, I just disagree with the wording.
Makes sense, but I still think that it's a bit strange. If this is only needed for coroutine iterators, then maybe the initial call could be moved into the non-member begin() function instead of the constructor.
I think that if a coroutine needs to be called four times, it should have to be called four times. Not three times and once in the constructor.
How do you tell the difference between the calling constructor and the non-calling constructor for a coroutine<void()>?
It looks like the current boost implementation will always call the coroutine immediately if it's a coroutine<void ()>.
I would prefer that the default behavior is that the coroutine does NOT get called immediately.
That just makes it easier to use the coroutine as a functor, for example as a callback to an observer pattern.
But you could do the same thing by checking the coroutine itself instead of checking the result of the operator(). I don't see what the benefit is. All you get is essentially that you can write
while (some_coroutine()) {}
instead of
for (; some_coroutine; some_coroutine()) {}
I find it confusing if a functor doesn't return the result of it's call. I understand why it shouldn't return the result, but I would prefer that it return nothing instead.
As I understand it the reason for why it doesn't return the result is so that there is only one way to return values from a coroutine, right?
If that is the case then I would prefer it if we require that the last returned value from a coroutine has to be returned with a normal return statement, like it was done in the old boost.coroutine.
Then we could make operator() return that value.
And then a coroutine object is more useful as a functor for algorithms like std::transform.
It also makes it so that you can only pass a noexcept function to a coroutine if you know that it will complete before being destroyed. Which is not unreasonable, but should be a written requirement.
Am Samstag, 16. März 2013 20:31:35 UTC+1 schrieb maltes...@gmail.co
If that is the case then I would prefer it if we require that the last returned value from a coroutine has to be returned with a normal return statement, like it was done in the old boost.coroutine.
in some cases the coro-fn would become a wired structure - for instance for returning values from loops - then the last result must be returned by a return statement.
--
---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/3g6ZIWedGJ8/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
Could you also post a link to the discussions on the boost mailing list? I don't want to ask questions that have already been discussed.
Has there been any thought to yielding without a value? It might be useful if the coroutine is computing something that takes a while but it wants to give up control of the thread every now and then. All that would be required is that the coroutine has a operator() that takes no arguments. A coroutine can already be in a state where it doesn't have a result from the call, so this wouldn't complicate things all that much.
I also think that this is needed because there is this weird asymmetry with the first call from the constructor where the function inside of the coroutine may be called without arguments. But it has to return a value anyway.
As for the noexcept thing: What I meant is that if your function is noexcept, you have to be certain that the coroutine will finish, because stack unwinding will terminate the program. You can try that with the stack unwinding example from the boost website: If you make the function in there be noexcept the program will call terminate().
> the proposal tells nothing about threads - thread are out out of scope
I don't think you've got a choice in this matter.
You still have to specify what coroutine resume/yield does to
thread_local variables. Coroutine-specific variables are difficult to
implement on some platforms. If coroutines just use the thread_local
variables of the executing thread, some currently valid compiler
optimizations are broken. For example, with coroutines, the address
of a thread_local variable can change during a function call, which is
currently impossible.
One way out of this is to specify that a coroutine must not be resumed
on a different thread. But this is awfully restrictive.
Some ABIs use a thread status register that it is used to refer to thread local storage. Compilers do assume that the value of this register is preserved across function calls and optimize accordingly (for example hoisting thread local storage address computations). If a coroutine is moved from one thread to another, the assumption obviously does not hold. In fact it would be wrong for a coroutine to preserve the thread status register. This means that coroutines put a constraint on compiler optimizations. This constraint should be mentioned.
No, ordinary (standard compliant) code cannot switch to another thread, so the optimization is normally legal.
On 3/15/13, Oliver Kowalke <oliver....@gmail.com> wrote:
> this is the first version of the proposal to add coroutines to C++.
> The pdf can be found here: http://ok73.funpic.de/coroutine.pdf
Could you make a comparison to other coroutine libraries?
For example, the original task library has been abandoned. Why?
What does your proposal do differently?
Hi,
On Monday, March 18, 2013 9:40:25 PM UTC+9, Oliver Kowalke wrote:Am Montag, 18. März 2013 13:20:15 UTC+1 schrieb Giovanni Piero Deretta:
No, ordinary (standard compliant) code cannot switch to another thread, so the optimization is normally legal.
OK, I see that thread_local can have namespace-scope, be a static class data member and a local variable. The last one can be moved to other threads via stack swapping and should not be used in the context of coroutines if coroutine is migration to an other thread.
have you read http://www.crystalclearsoftware.com/soc/coroutine/coroutine/coroutine_thread.html ? Not only automatic thread local variables but any kinds of thread local variables might become problematic if a coroutine migrates between threads.I think that our only option is to specify "a coroutine should not migrate between multiple threads."
The wording like "thread-local storage should not be used if a coroutine migrates between threads" eventually prohibits any use of functions and classes of which the implementation detail is not known to us. Functions and classes might use thread-local storage as their internal implementation detail. In theory, even an implementation of std::vector is allowed to use thread-local storage internally for any purpose. Therefore, as far as strict compliance with the specification is concerned, std::vector cannot be used in a coroutine if the coroutine migrates between threads. This is pretty much the same as the conclusion that a coroutine should not migrate between threads.
X::instance()->abc(); // may call the Y installed at this thread or
the previous one, depending on the phase of the moon.
I showed in a previous post. To make it short, the compiler, instead
of recomputing the address of X::y after the call to c(), can reuse
the previously computed value, as normally its address cannot change.
Hi,
this is the first version of the proposal to add coroutines to C++.
The pdf can be found here: http://ok73.funpic.de/coroutine.pdf
so long,
Oliver
This is a bad proposal, do not do it like that!
[quote]This design decision makes the code using std::coroutine<> let look more symmetric.[/quote]
More symmetric does not make it more readable. Yuck. Do we really want to make C++11 more confusing?
What is the use case for which this facility would be useful?
Well motivated proposals have a much easier time making it through
the process.
Right, that looks much better. Do you want to add it as an alternative in your proposal?
Right, that looks much better. Do you want to add it as an alternative in your proposal?
Isn't await (N3564) going to deliver mostly the same functionality (but putting the "local" variables into a compiler-generated object).
it does not provide some kind of escape-and-reenter of loops
--
---
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-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
2013/3/23 <denis...@bredelet.com>Right, that looks much better. Do you want to add it as an alternative in your proposal?
added as alternative design to proposal
My concern with:std::pull_coroutine <int> c( [&](std::push_coroutine <int> & c) {is that the type of the push_coroutine is not indicated in the pull_coroutine, shouldn't it be std::pull_coroutine <int(int)>?
My concern with:std::pull_coroutine <int> c( [&](std::push_coroutine <int> & c) {
is that the type of the push_coroutine is not indicated in the pull_coroutine, shouldn't it be std::pull_coroutine <int(int)>?
--
---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/3g6ZIWedGJ8/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
As N3564 describes 'After suspending, a resumable function may be resumed by the scheduling logic of the runtime...' - seams not to be
equivalent cooperative scheduling.
Here is the entire thing as working code in C# which already has the await mechanism. The iterate_tree function works exactly the same as with the coroutines.
Obviously the compiler needs to create an object or a side-stack to hold the state of these functions while they are paused, but that is no different from the coroutines AFAICT.
The Task object that I use here to control the process would be replaced with a future. The future class doesn't do much more than store pointers to the code and the data of the resumable function, so the added complexity should be small if the compiler can eliminate any unused features included in std::future.
And whatever else you do, submit the paper. There is nothing worse
that promising a paper on a public list like this one, and then not
delivering it. Since it is a late paper, you can just sent it to me
directly. And don't wait until the last minute. If you improve the
paper later, just send that along.
I'm currently working to support checkpointing of coroutines - I think it would be useful too?!
Should I add it to the proposal?
On Friday, March 22, 2013 10:16:39 AM UTC+1, Oliver Kowalke wrote:I'm currently working to support checkpointing of coroutines - I think it would be useful too?!
Should I add it to the proposal?No, absolutely not. While checkpointing might be useful, I think it is far too complicated and dangerous to be added to the initial proposal. Adding checkpointing means you have to define what this means:checkpoint cp;std::vector<int> v(5, 5);
std::coroutine< int() > c(
[&](std::coroutine< void( int) > & c) {
int i = 0;
cp = c.checkpoint(); // coroutine checkpoint
c().get();
while ( true) {
c( ++i);
});
c.rollback( cp);What happens to the vector on rollback? Is it destroyed and reconstructed?
Is its initialization skipped the second time around?
Is this just undefined?
Is it the same behavior as setjmp/longjmp exhibit? (I think setjmp/longjmp would be undefined in this case according to 18.10p4.)
In general, is checkpoint()/rollback() equivalent to setjmp and longjmp within the context of the coroutine, i.e. it only changes execution position, not values? Or would values be changed in some way?
These seem to me to be non-trivial issues that would only muddle the proposal you have now. And I see no reason why checkpointing couldn't be added later in a separate proposal.