Hello Nat, Am 10.09.2012 19:12, schrieb Nat Linden:
This sounds like a classic collision between theory and practice. All I can say is that we heavily rely on the functionality provided by Giovanni's 'future' objects, and I cannot convert to a library that does not provide something equivalent.
Our use case, as I said at C++ Now last May, seems fairly straightforward. Giovanni's Coroutine library allows us to write code that invokes asynchronous operations, yet retains the simplicity and maintainability of using blocking operations. Cooperative context switching is essential to us: we cannot use a new thread for this purpose due to the prohibitive cost of discovering and protecting accesses to all shared data objects.
with boost.fiber you don't spawn a new thread. it is another abstraction over context switching /cooperative multitasking. wikipedia: 'Fibers describe essentially the same concept as coroutines. The distinction, if there is any, is that coroutines are a language-level construct, a form of control flow, while fibers are a systems-level construct, viewed as threads that happen not to run concurrently. Priority is contentious; fibers may be viewed as an implementation of coroutines[1], or as a substrate on which to implement coroutines.'
I've been using the term 'coroutine' for this tactic because Giovanni does. Perhaps that's an abuse of the theoretical concept of "coroutine;" if so I apologize. But whatever you call it, we need that API, or something very like it.
I don't know your code but if you need 'future' semantics I suggest boost.fiber (http://ok73.ok.funpic.de/boost/libs/fiber/doc/html/, http://ok73.ok.funpic.de/boost.fiber.zip). boost.fiber provides lightweight threads using boost.context (context switching). Thank you for the links. I will look it over.
I'm sorry to say that I've overlooked boost.fiber until now because (a) I don't believe it was announced or discussed on the boost-users mailing list, and I'm not on the boost developers' list; and (b) when I see the word "fiber" in the context of program context, I immediately think "Windows-specific." I will be glad to be wrong in that assumption.
Previously I called it stratum (stratified) but members of the developer list requested to rename it to fiber
The lib provides classes like mutex, condition-variables, event-variables and futures. You can use it like boost.thread - but it provides cooperative multitasking. Hmm! Dangerous though it is to shoot off my mouth before even starting to read about boost.fiber, I immediately wonder about the need for mutex in the context of cooperative context switching. Time to stop speculating and start reading. ;-)
The mutex in boost.fiber has nothing to do with the mutext of boost.thread (e.g. pthread_mutex etc.) It is an abstraction over context switching. my intention was to write code for cooperative mutlitasking as I would do it for threads.
======================================================================== What follows are responses to specific documentation comments.
"The maximum number of arguments of coroutine-function is 10." it's a limitation of boost.tuple Good. Mentioning that in the documentation's Note would permit an interested party to consult Tuple documentation to discover how to lift the limit if needed.
OK - I add that the limit is raised by boost.tuple
"An exception thrown inside coroutine-function (transfered via exception-pointer - see Boost.Exception for details and requirements) will be re-thrown by coroutine<>::operator()()." I gave the hint that the docu of boost.exception will describe all the requirements. Thank you. You're correct that this permits me to research it myself. Anything more is simply to inform a lazy reader. :-)
some remarks added to new version of boost.coroutine
Usally the used should not let an exceptio nescape from a coroutine-fn. That's an interesting remark, and I would like to understand better why you say that. I may well instantiate a coroutine with the assumption that low-level exceptions in (code called by) coroutine-function will be handled by the code that instantiates the coroutine. Am I misguided?
I suggest that the coroutine-fn handles the exceptions itself - exceptions escaped from coroutine-fn are catched and in the worst case re-thrown as unknown_excpetion
In the worst case you get an exception of type unknown_exception rethrown. Good. It might be worth asserting in the documentation that -- even though coroutine-function should ideally have a top-level try/catch construct -- the library ensures that an exception in coroutine-function will never be silently swallowed. done
"The first argument of generator-function must be of type generator<>::self_t, ..."
The "only" argument? You could bind parameters to function entry As I said in my previous note, it seems worth a few words in the Generator documentation to distinguish between the generator-function (possibly a bind() expression using any of several available bind() implementations) and the C++ function bound by that bind() expression. While the C++ function is of primary interest to the coder, and the bind() expression seems a mere detail -- the coroutine library sees it differently. Requirements on the coroutine-function and generator-function apply to the actual expression (possibly a bind() expression) passed to the coroutine or generator constructor. The bound C++ function is effectively invisible to the coroutine library.
It is from that perspective that I would clarify that generator-function must accept exactly one argument of type generator<>::self_t. I thin I've explained it in a more detail in the new version (at least I hope so)
"generator-function is invoked the first time inside the constructor of generator."
That makes no sense to me. Is the value passed to the first yield() call simply discarded? If so, why? I tried to express that a generator must be tested before you can use it:
gen_t gen(...); if ( gen) { int x = gen(); }
in order to know if gen is valid (== it will return a value) the return-value must be fetched from the generator-fn. Oh! Light belatedly dawns. So a generator object constructed with a generator-function with an empty body would immediately test 'false'. yes - because it will not return a (usefull) value
That implies that the generator object buffers the value passed to yield() until operator() is called to retrieve it. That seems worth mentioning. It would have corrected my incomplete mental model. I've some notes in the docu - maybe not explicit enought?!
"If generator-function can not return valid values anymore generator<>::self_t::yield_break() should be called. This function returns the execution control back to the caller and sets the generator to be complete (is_complete() returns true)."
is_complete() is not documented for boost::coro::generator. yes - you have to use generator<>::unspec_bool() or generator<>::operator!() So the reference to is_complete() in the paragraph quoted above should be removed or amended. done
regards, Oliver