
Coroutine should be accepted into Boost conditionally. My hesitation comes from the seemingly significant design and interface changes occurring during the review. I'm unsure how the current state compares to the library I reviewed. This leaves me wondering what is being accepted. I don't think the functionality of the library is changing, so I'm confident in accepting it on that basis. However, I think that a mini-review is needed to verify the finished design and documentation before final acceptance. ***** What is your evaluation of the design? I dislike the namespace. "coro" is meaningless and awkward. The namespace name should be "coroutines" (since there is a class named "coroutine", it must be plural). What users may choose as an alias is another matter. I wonder whether assertions can be added to flag calls of operator() from within a coroutine or generator. I know that's undefined behavior, but an assertion will catch the mistake in non-optimized builds. ***** What is your evaluation of the implementation? The source has major whitespace issues. It looks like a botched tab-to-spaces conversion. The move assignment operators do a self-assignment check. That is a "performance bug", as Howard Hinnant puts it. There are no comments in the code. This means Oliver's own maintenance will be harder, but it particularly means that it will be harder for anyone else to pick up the library's maintenance to help or replace Oliver in the future. The code looks well factored, though I certainly didn't spend a long time studying it. ***** What is your evaluation of the documentation? There is no Motivation section. The documentation must catch the attention of the reader right on the first page. Why should anyone care about this library? What can it do for them? Why should anyone spend time reading the rest of the documentation? Overall, the documentation is a good introduction to the library. The examples are appropriate for tutorial purposes, but there is a complete lack of non-trivial examples to illustrate how the library can be employed. What can one do with this library that isn't possible otherwise? What is better, according to any number of criteria like less code, faster, etc., using this library? How much of the interface remains what was designed by Giovanni Piero Deretta? Given all of the changes during the review, I wonder how much remains. The "Executing a coroutine" section of coroutine.html could do with a sentence about the coroutine-function's signature as, say, a new, third sentence in the first paragraph: "The coroutine-function can take arguments and return values as discussed in the next section." The "Exceptions in coroutine-function" section of coroutine.html, and the corresponding section of generator.html, suggests that exceptions must be thrown using Boost.Exception. Is that the intent? If so, you need to make that point clear. I suppose that isn't true in C++11. Also, what is the namespace of forced_unwind? This section is the first mention of it, so one is left to wonder if it belongs to the library or not. The "FPU preserving" sections of coroutine.html and generator.html imply that the library allows to prevent preserving the FPU registers, but gives no information about how that is done or where to read more about it. Also, the note, in those sections, references "ABIs", but gives no references and fails to specify which ABIs are meant. The coroutine and generator synopses, in coroutine/coroutine.html and generator/generator.html, are confusing. The definition of self_t refers to undefined types T and R. The synopsis of coroutine suggests that the type is not copyable Some detailed comments are at the end of this message. ***** What is your evaluation of the potential usefulness of the library? I think this library will be extremely useful. I think it could have profound implications for the design of libraries that rely on multiple threads today. Indeed, there is a use case in some current code in one of our applications that, I think, would be simplified yet improved by using this library. - Did you try to use the library? With what compiler? Did you have any problems? Unfortunately, and despite the lateness of my review, I've had no time to try it. - How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I spent several hours reading the documentation, and a fraction of that looking over the code. ***** Are you knowledgeable about the problem domain? No. ___________________ Documentation Comments ________________ coroutine/coroutine.html The illustration of make_coroutine() is accompanied by no description, so f() should be omitted. It adds nothing one cannot infer from the signature and raises questions about what c and, in turn, f() actually does. In the first warning, s/coroutine/a coroutine/. In the second, s/::FlsAlloc(), ::FlsFree()/::FlsAlloc() and ::FlsFree()/. s/stack-allocator/stack allocator/ Odd internal spacing for parentheses: one space after the open parenthesis, and no space before the close parenthesis. s/operator()()/operator()/ s/flags, and stack and instruction/flags, stack, and instruction/ s/value of local variable is preserved/value of local variable i is preserved/ "yield was called so we returned" is confusing. The coroutine hasn't been called by that point. That comment should probably be, "while the coroutine hasn't returned" (or "finished"). In Transfer of data, the last sentence of the second paragraph (after the note) is rather confusing and includes a grammatical error. I suggest this instead: "The arguments passed to coroutine<>::operator(), in one coroutine, are passed to coroutine<>::self_t::yield() in the other coroutine, except for the first invocation of coroutine<>::operator(), which passes them to the coroutine- function, instead." The example in Transfer of data could use with a few more comments. Specifically, "int x = c(7)" could use the comment, "x is 7 because c(7) passes 7 to fn, where self.yield(i) passes i == 7 back", and "x = c(10)" could use the comment, "x is 10 because c(10) causes "self.yield(i)" to return 10 in fn(), so fn() returns j == 10". While you're at it, s/j == 10 because c( 10) in main()/j == 10 because of c(10) in main()/. The coroutine-function with multiple arguments example deserves some comments explaining why yield() gets one int argument and returns a two-int tuple and why the function returns one int, to tie those aspects into the Signature template argument. The Exit a coroutine-function section discusses yield_break(), but the example calls yield_return(). In the Important note within Exit a coroutine-function, s/complete (can not resumed/complete and cannot be resumed/. In Stack unwinding, s/(stack is unwound by default)/(the default)/. In FPU preserving's note, s/ABIs the/ABIs, the/. ________________ coroutine/coroutine/coroutine.html In the Effects of the template constructor, s/stack before destructing it/stack before releasing it/. In the Effects of the function call operator, the wording can be improved: "Invokes the coroutine-function with the supplied arguments, returning the argument passed to self_t::yield()." Likewise, the Throws clause: "Any exception emitted by the coroutine-function or coroutine_terminated if self_t::yield_break() is called." The Effects of self_t::yield() can be improved: "Returns execution control to the calling context, arranging for the calling context's operator() to return r." (You need to make the argument list "(R r)" for that description.) Next, yield() needs a "Returns" clause: "Returns: " Arguments passed to the calling context's operator()." Change the Effects clause for yield_break() to: "Marks the coroutine complete and returns execution control back to the calling coroutine in such a way as to cause the calling coroutine's operator() to emit a coroutine_terminated exception." ______________ generator.html s/yielding a values/yielding values/ s/and can be returned/, so they can be returned/ make_generator() is not introduced. The illustration of make_generator() is accompanied by no description, so f() should be omitted. It adds nothing one cannot infer from the signature and raises questions about what c and, in turn, f() actually does. (If you choose to keep it, perhaps "g" is a better identifier for a generator.) In the second note, s/::FlsAlloc(), ::FlsFree()/::FlsAlloc() and ::FlsFree()/. Also, that same information is marked as a warning for coroutine. In the warning, s/If generator/If a generator/. There are no comments in most of the examples on this page. The fifth paragraph of Executing a generator needs to be rewritten to fix mistakes and improve clarity: "A generator is usually called inside a loop. The generator- function calls generator<>::self_t::yield() to produce a value. When the generator is called a second or subsequent time, the generator-function resumes execution after the yield() call. If a generator-function must be called no more, it can call generator<>::self_t::yield_break()." The sixth paragraph also needs to be rewritten: "The generator-function is invoked the first time from the generator's constructor. generator<>::operator() can be called while the generator is valid (while generator<>::operator unspecified_bool_type() returns true). When the generator-function cannot return more values, it can return a value or call generator<>::self_t::yield_break(). The latter returns execution control back to the caller and marks the generator complete. Note that generator<>::operator() does not thrown an exception, unlike a coroutine." (It isn't clear whether returning from a generator-function marks a generator complete.) In the last Executing a generator example, the "yield was called so we returned" comment within main() is a bit unclear. I suggest, "calls fn()" on g's construction and "fn()called self.yield(), so control returned here" on "while (g)". In the Important note within Exit a generator-function, s/complete (can not resumed/complete and cannot be resumed/. _______________ generator/generator.html The Effects clause of operator() would be better separated into an Effects and a Return clause: "Effects: " Invokes the generator-function." "Returns: " The value passed to self_t::yield() by the generator-function." Likewise, for self_t::yield(): "Effects: " Returns execution control to the calling context such that generator<>::operator() returns r in the calling context. "Returns: " The arguments passed to generator<>::operator() in the " calling context." The Effects clause for self_t::yield_break() is incorrect. I suggest the following: "Marks the generator complete and returns execution control to the calling context." _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

I dislike the namespace. "coro" is meaningless and awkward.
STL's namespace is 'std' I tought it's ok if I do it liekwise.
I wonder whether assertions can be added to flag calls of operator() from within a coroutine or generator. I know that's undefined behavior, but an assertion will catch the mistake in non-optimized builds.
The base class asserts (BOOST_ASSERT) pre-conditions in its functions like resume()/suspend(). In non-debug builds (optimization doesn't matter) the assertions remain until compiler flag BOOST_DISABLE_ASSERTS is applied.
The move assignment operators do a self-assignment check. That is a "performance bug", as Howard Hinnant puts it.
OK, thx for the hint.
The examples are appropriate for tutorial purposes, but there is a complete lack of non-trivial examples to illustrate how the library can be employed. What can one do with this library that isn't possible otherwise? What is better, according to any number of criteria like less code, faster, etc., using this library?
sub-directory contains 'example' contains code demonstrating how to use a coroutine together with an streambuf in order to provide an non-blocking istream. The stream reads data from a socket demultiplexed by boost::asio::io_service. Might this be 'non-trivial'? I think the documentation should describe simple examples and the 'example' sub-directory contains more complex ones (but harder to describe).
How much of the interface remains what was designed by Giovanni Piero Deretta? Given all of the changes during the review, I wonder how much remains.
After looking over the suggested modifications It seams that not very much of Giovanni's design will remain (self_t will be removed from coroutine-fn signature).
The "Exceptions in coroutine-function" section of coroutine.html, and the corresponding section of generator.html, suggests that exceptions must be thrown using Boost.Exception. Is that the intent? If so, you need to make that point clear.
It should, if you do not follow the recommendations in boost.exception the exceptions thrown inside the coroutine-fn will be re-thrown with type 'unknown_exception'.
I suppose that isn't true in C++11.
I don't know. At least for compilers no implementing C++11 it should be true.
Also, what is the namespace of forced_unwind? This section is the first mention of it, so one is left to wonder if it belongs to the library or not.
'forced_unwind' is in 'detail' namespace. The user should not use it because the exception is used to unwind the stack.
The "FPU preserving" sections of coroutine.html and generator.html imply that the library allows to prevent preserving the FPU registers, but gives no information about how that is done or where to read more about it.
OK
Also, the note, in those sections, references "ABIs", but gives no references and fails to specify which ABIs are meant.
Should I not expect that the user knows what an ABI is and which ABI is used on its platform? Should I add descriptions of all available ABIs? regards, Oliver

Oliver Kowalke wrote:
I dislike the namespace. "coro" is meaningless and awkward.
STL's namespace is 'std' I tought it's ok if I do it liekwise.
Should your library be standardized, it may appear in the std namespace or some subnamespace which could well be std::coro. However, Boost has policies on namespaces and "coro" doesn't fit.
The examples are appropriate for tutorial purposes, but there is a complete lack of non-trivial examples to illustrate how the library can be employed. What can one do with this library that isn't possible otherwise? What is better, according to any number of criteria like less code, faster, etc., using this library?
sub-directory contains 'example' contains code demonstrating how to use a coroutine together with an streambuf in order to provide an non-blocking istream. The stream reads data from a socket demultiplexed by boost::asio::io_service. Might this be 'non-trivial'?
I didn't even look under libs to see the examples until you prompted me. However, I don't see that example. Nevertheless, it sounds interesting.
I think the documentation should describe simple examples and the 'example' sub-directory contains more complex ones (but harder to describe).
All of the examples, or most if there are a great many, should be described in the documentation, with links to the full source. This is true for two reasons. First, those reading the documentation are not looking through the source tree. Second, the documentation affords the opportunity to discuss the examples freely, giving an introductory explanation as well as dissecting and discussing interesting parts of each. As there are no comments in the examples, documentation is even more important. Even if you added comments, which is reasonable, the examples should be introduced, at least, in the documentation.
How much of the interface remains what was designed by Giovanni Piero Deretta? Given all of the changes during the review, I wonder how much remains.
After looking over the suggested modifications It seams that not very much of Giovanni's design will remain (self_t will be removed from coroutine-fn signature).
Your mention of his design should be moved to the Acknowledgements section, then.
The "Exceptions in coroutine-function" section of coroutine.html, and the corresponding section of generator.html, suggests that exceptions must be thrown using Boost.Exception. Is that the intent? If so, you need to make that point clear.
It should, if you do not follow the recommendations in boost.exception the exceptions thrown inside the coroutine-fn will be re-thrown with type 'unknown_exception'.
You need to be specific about those points, then.
I suppose that isn't true in C++11.
I don't know. At least for compilers no implementing C++11 it should be true.
If you missed it, I'm referring to std::exception_ptr, std::current_exception(), and std::rethrow_exception().
Also, what is the namespace of forced_unwind? This section is the first mention of it, so one is left to wonder if it belongs to the library or > not.
'forced_unwind' is in 'detail' namespace. The user should not use it because the exception is used to unwind the stack.
This is confusing then. You reference the exception type by name, illustrate catching it by name, as well as having a catch-all handler, thereby suggesting that users can write that same set of handlers, but the type name is in the detail namespace, so users shouldn't reference it. If users shouldn't know the name, then you shouldn't mention it. Instead, you should tell them they mustn't absorb unknown exceptions because they'll absorb an important exception used by the coroutine implementation. Thus, you should illustrate what's acceptable by: try { // coroutine-using code } catch (user-known-exception-type const & e) { // rethrow or absorb e } catch (...) { // react to exception throw; // rarely good to absorb unknowns } If you intend that they should be able to differentiate between your exception and others, then the name shouldn't be in the detail namespace and you should show the following: try { // coroutine-using code } catch (boost::coroutines::forced_unwind const &) { throw; } catch (user-known-exception-type const & e) { // rethrow or absorb e } catch (...) { // react to exception throw; // rarely good to absorb unknowns }
Also, the note, in those sections, references "ABIs", but gives no references and fails to specify which ABIs are meant.
Should I not expect that the user knows what an ABI is and which ABI is used on its platform?
I didn't mean to suggest you needed to define ABI, though a link to Wikipedia or similar location wouldn't hurt. Instead, my concern is that you refer to "ABIs" as if to say all extant ABIs. I doubt you've studied them all, so that's too broad. Perhaps you mean, "the ABIs on all supported platforms".
Should I add descriptions of all available ABIs?
No, but links to details wouldn't hurt if practicable. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.
participants (2)
-
Oliver Kowalke
-
Stewart, Robert