|
Boost Users : |
Subject: Re: [Boost-users] [Review.Coroutine] Coroutine review comments
From: Oliver Kowalke (oliver.kowalke_at_[hidden])
Date: 2012-09-10 13:29:16
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
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net